From 7ceb9e571d8231dfd5bc25d50eb5bf71f0ea3c4b Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 20 Jul 2026 15:11:38 -0500 Subject: [PATCH] Scene prefabs --- assets/scenes/test.json | 39 +---------- src/dusk/scene/CMakeLists.txt | 1 + src/dusk/scene/scene.c | 11 +++ src/dusk/scene/scene.h | 25 +++++-- src/dusk/scene/sceneprefab.c | 68 +++++++++++++++++++ src/dusk/scene/sceneprefab.h | 67 ++++++++++++++++++ src/dusk/scene/sceneprefablist.h | 13 ++++ .../entity/component/overworld/entityplayer.c | 19 +++++- .../entity/component/overworld/entityplayer.h | 20 +++--- src/duskrpg/entity/gameprefablist.h | 2 + src/duskrpg/entity/prefab/CMakeLists.txt | 1 + .../prefab/entityprefaboverworldcamera.c | 37 ++++++++++ .../prefab/entityprefaboverworldcamera.h | 25 +++++++ src/duskrpg/scene/CMakeLists.txt | 2 + src/duskrpg/scene/gameprefablist.h | 16 +++++ src/duskrpg/scene/prefab/CMakeLists.txt | 9 +++ .../scene/prefab/sceneprefaboverworldscene.c | 26 +++++++ .../scene/prefab/sceneprefaboverworldscene.h | 20 ++++++ 18 files changed, 349 insertions(+), 52 deletions(-) create mode 100644 src/dusk/scene/sceneprefab.c create mode 100644 src/dusk/scene/sceneprefab.h create mode 100644 src/dusk/scene/sceneprefablist.h create mode 100644 src/duskrpg/entity/prefab/entityprefaboverworldcamera.c create mode 100644 src/duskrpg/entity/prefab/entityprefaboverworldcamera.h create mode 100644 src/duskrpg/scene/gameprefablist.h create mode 100644 src/duskrpg/scene/prefab/CMakeLists.txt create mode 100644 src/duskrpg/scene/prefab/sceneprefaboverworldscene.c create mode 100644 src/duskrpg/scene/prefab/sceneprefaboverworldscene.h diff --git a/assets/scenes/test.json b/assets/scenes/test.json index a48285d2..f1b2899e 100644 --- a/assets/scenes/test.json +++ b/assets/scenes/test.json @@ -1,48 +1,11 @@ { + "extend": "OVERWORLDSCENE", "entities": [ - { - "name": "camera", - "components": [ - { - "type": "POSITION", - "x": 10, "y": 10, "z": 10, - "lookAt": { "x": 0, "y": 0, "z": 0 } - }, - { - "type": "CAMERA", - "projType": "PERSPECTIVE", - "nearClip": 0.1, "farClip": 1000, - "fov": 0.9 - } - ] - }, { "components": [ { "type": "POSITION", "x": 0, "y": 1, "z": 0, "parent": "camera" } ] }, - { - "name": "player", - "extend": "PLAYER" - }, - { - "components": [ - { - "type": "POSITION", - "x": 0, "y": 0, "z": 0.85, - "parent": "player", - "scale": { "x": 0.7, "y": 0.7, "z": 0.7 } - }, - { - "type": "RENDERABLE", - "renderType": "SHADER_MATERIAL", - "priority": 0, - "shaderType": "UNLIT", - "color": { "r": 80, "g": 160, "b": 255, "a": 255 }, - "displayState": { "cull": false, "depthTest": true, "blend": false } - } - ] - }, { "name": "testArea", "components": [ diff --git a/src/dusk/scene/CMakeLists.txt b/src/dusk/scene/CMakeLists.txt index b5764583..2eb8bab9 100644 --- a/src/dusk/scene/CMakeLists.txt +++ b/src/dusk/scene/CMakeLists.txt @@ -6,4 +6,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC scene.c + sceneprefab.c ) \ No newline at end of file diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index 264e7031..d7bd8751 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -15,6 +15,7 @@ #include "entity/component/display/entityposition.h" #include "entity/component/display/entityrenderable.h" #include "physics/triggersystem.h" +#include "sceneprefab.h" #include "ui/ui.h" #include "console/console.h" @@ -251,6 +252,16 @@ errorret_t sceneDeserialize( entitymanager_t *mgr = &SCENE_MANAGER.scenes[id].entities; + yyjson_val *extendVal = yyjson_obj_get(json, "extend"); + if(extendVal) { + errorChain(scenePrefabResolveAndApply(id, yyjson_get_str(extendVal))); + } + + yyjson_val *prefabVal = yyjson_obj_get(json, "prefab"); + if(prefabVal) { + errorChain(scenePrefabResolveAndApply(id, yyjson_get_str(prefabVal))); + } + yyjson_val *entities = yyjson_obj_get(json, "entities"); if(!entities) errorOk(); diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h index f675f700..096cc475 100644 --- a/src/dusk/scene/scene.h +++ b/src/dusk/scene/scene.h @@ -141,14 +141,29 @@ errorret_t sceneSerialize( ); /** - * Deserializes entities from the given JSON object's "entities" array into - * a scene, creating one new entity per array entry (see - * entityDeserialize()), then resolves every entity's POSITION "parent" + * Deserializes a scene's optional "extend"/"prefab" and its entities from + * the given JSON object. + * + * If present, "extend" and then "prefab" are each resolved and applied as + * a scene prefab (see scenePrefabResolveAndApply()) before "entities" is + * processed -- both keys do the same thing (resolve a named scene + * prefab, C-coded first then a "scenes/.json" asset, and apply it + * to this scene); "extend" is the conventional key when this JSON is + * itself a scene prefab definition inheriting from a parent, "prefab" is + * the conventional key when a plain scene wants to be defined by a + * prefab. Since a scene prefab just spawns more entities, there's no + * collision risk the way there is for entity components -- this scene's + * own "entities" are simply added on top of whatever the prefab spawned. + * + * Then creates one new entity per "entities" array entry (see + * entityDeserialize()), and resolves every entity's POSITION "parent" * name reference (see sceneDeserializeResolveParent()) now that all - * entities exist and are named. No-op if json has no "entities" array. + * entities exist and are named. No-op for "entities" if json has no such + * array. * * @param id The ID of the scene to deserialize into. - * @param json The JSON object to read the scene's "entities" key from. + * @param json The JSON object to read the scene's "extend"/"prefab"/ + * "entities" keys from. * @return Error state. */ errorret_t sceneDeserialize( diff --git a/src/dusk/scene/sceneprefab.c b/src/dusk/scene/sceneprefab.c new file mode 100644 index 00000000..695946fa --- /dev/null +++ b/src/dusk/scene/sceneprefab.c @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "sceneprefab.h" +#include "scene.h" +#include "asset/asset.h" +#include "util/string.h" +#include "assert/assert.h" +#include "yyjson.h" + +// Pulls in every scene prefab's real header (sceneprefablist.h's one-time +// #include lines) exactly once -- each header's #pragma once then makes +// the array-building pass below a no-op for those lines, leaving only +// bare X(...) invocations to expand. +#define X(enumName, extends, apply) // do nothing +#include "sceneprefablist.h" +#undef X + +sceneprefab_t SCENE_PREFABS[] = { + #define X(enm, extendsName, applyFn) \ + { .name = #enm, .extends = extendsName, .apply = applyFn }, + #include "sceneprefablist.h" + #undef X + + // Sentinel: name[0] == '\0' marks the end for callers iterating without + // a separate count. + { 0 } +}; + +errorret_t scenePrefabInit( + const sceneid_t sceneId, + const sceneprefab_t prefab +) { + assertNotNull(prefab.apply, "Prefab apply function cannot be null"); + if(prefab.extends[0] != '\0') { + errorChain(scenePrefabResolveAndApply(sceneId, prefab.extends)); + } + errorChain(prefab.apply(sceneId)); + errorOk(); +} + +errorret_t scenePrefabResolveAndApply( + const sceneid_t sceneId, + const char_t *name +) { + for(size_t i = 0; SCENE_PREFABS[i].name[0] != '\0'; i++) { + if(!stringEquals(SCENE_PREFABS[i].name, name)) continue; + errorChain(scenePrefabInit(sceneId, SCENE_PREFABS[i])); + errorOk(); + } + + char_t assetPath[ASSET_FILE_NAME_MAX]; + stringFormat(assetPath, ASSET_FILE_NAME_MAX - 1, "scenes/%s.json", name); + + assetentry_t *entry = assetLock(assetPath, ASSET_LOADER_TYPE_JSON, NULL); + errorret_t ret = assetRequireLoaded(entry); + if(errorIsOk(ret)) { + ret = sceneDeserialize(sceneId, yyjson_doc_get_root(entry->data.json)); + } + assetUnlockEntry(entry); + errorChain(ret); + + errorOk(); +} diff --git a/src/dusk/scene/sceneprefab.h b/src/dusk/scene/sceneprefab.h new file mode 100644 index 00000000..0e766f0d --- /dev/null +++ b/src/dusk/scene/sceneprefab.h @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "scenebase.h" +#include "error/error.h" + +#define SCENE_PREFAB_NAME_MAX 32 + +/** + * Applies a prefab to a scene, e.g. spawning and configuring whatever + * entities make up the scene. + * + * @param sceneId The ID of the scene to apply the prefab to. + * @return Error state. + */ +typedef errorret_t (*sceneprefabapply_t)(const sceneid_t sceneId); + +typedef struct { + char_t name[SCENE_PREFAB_NAME_MAX]; + char_t extends[SCENE_PREFAB_NAME_MAX]; + sceneprefabapply_t apply; +} sceneprefab_t; + +/** + * The C-coded scene prefab registry, built from sceneprefablist.h. + * Terminated by a sentinel entry whose name[0] is '\0' -- iterate with a + * `for(...; SCENE_PREFABS[i].name[0] != '\0'; ...)` style loop rather + * than a fixed count. + */ +extern sceneprefab_t SCENE_PREFABS[]; + +/** + * Initializes a scene from a prefab, running its apply function. If the + * prefab declares a parent (prefab.extends is non-empty), that parent is + * resolved and applied first (see scenePrefabResolveAndApply()), so this + * prefab's own apply function runs on top of/after it. + * + * @param sceneId The ID of the scene to apply the prefab to. + * @param prefab The prefab to apply. + * @return Error state. + */ +errorret_t scenePrefabInit( + const sceneid_t sceneId, + const sceneprefab_t prefab +); + +/** + * Resolves a scene prefab by name and applies it to the given scene. + * First searches the C-coded SCENE_PREFABS[] registry (see + * sceneprefablist.h) for a matching name; if none match, falls back to + * loading "scenes/.json" as a JSON asset and applying it the same + * way a scene's own JSON definition is applied (see sceneDeserialize() + * and its "prefab"/"extend" keys). + * + * @param sceneId The ID of the scene to apply the prefab to. + * @param name The scene prefab name to resolve. + * @return Error state. + */ +errorret_t scenePrefabResolveAndApply( + const sceneid_t sceneId, + const char_t *name +); diff --git a/src/dusk/scene/sceneprefablist.h b/src/dusk/scene/sceneprefablist.h new file mode 100644 index 00000000..06bbe1dd --- /dev/null +++ b/src/dusk/scene/sceneprefablist.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +// Name (Uppercase) +// Extends (name of another scene prefab to apply first, or "" for none) +// Apply function + +// Game-specific scene prefabs +#include "scene/gameprefablist.h" diff --git a/src/duskrpg/entity/component/overworld/entityplayer.c b/src/duskrpg/entity/component/overworld/entityplayer.c index 8f1adc3e..8aa13a91 100644 --- a/src/duskrpg/entity/component/overworld/entityplayer.c +++ b/src/duskrpg/entity/component/overworld/entityplayer.c @@ -11,10 +11,13 @@ #include "entity/component/display/entitycamera.h" #include "entity/component/display/entityposition.h" #include "input/input.h" +#include "time/time.h" #include "util/memory.h" +#include "util/math.h" #define ENTITY_PLAYER_MOVE_SPEED_DEFAULT 4.0f #define ENTITY_PLAYER_JUMP_IMPULSE_DEFAULT 6.0f +#define ENTITY_PLAYER_TURN_SPEED_DEFAULT 10.0f // Below this squared magnitude, movement input is treated as "not moving" // and the player keeps facing whichever way it last faced, rather than @@ -32,6 +35,7 @@ void entityPlayerInit( player->moveSpeed = ENTITY_PLAYER_MOVE_SPEED_DEFAULT; player->jumpImpulse = ENTITY_PLAYER_JUMP_IMPULSE_DEFAULT; + player->turnSpeed = ENTITY_PLAYER_TURN_SPEED_DEFAULT; entityUpdateAdd(mgr, entityId, entityPlayerUpdate, componentId, NULL); } @@ -109,7 +113,16 @@ void entityPlayerUpdate( // faces the opposite way), which for a pure yaw rotation works out to // (sin(yaw), 0, cos(yaw)) -- invert that to solve for the yaw facing // moveDir. - rotation[1] = atan2f(moveDir[0], moveDir[1]); + float_t targetYaw = atan2f(moveDir[0], moveDir[1]); + + // Turn towards targetYaw at turnSpeed rad/s instead of snapping to it: + // wrap the shortest signed delta into [-PI, PI), then clamp the step. + float_t yawDelta = mathModFloat( + targetYaw - rotation[1] + (float_t)MATH_PI, 2.0f * (float_t)MATH_PI + ) - (float_t)MATH_PI; + float_t maxStep = player->turnSpeed * TIME.delta; + rotation[1] += mathClamp(yawDelta, -maxStep, maxStep); + entityPositionSetLocalRotation(mgr, entityId, posComp, rotation); } @@ -124,6 +137,7 @@ errorret_t entityPlayerSerialize( yyjson_mut_obj_add_real(doc, json, "moveSpeed", player->moveSpeed); yyjson_mut_obj_add_real(doc, json, "jumpImpulse", player->jumpImpulse); + yyjson_mut_obj_add_real(doc, json, "turnSpeed", player->turnSpeed); errorOk(); } @@ -143,6 +157,9 @@ errorret_t entityPlayerDeserialize( if((v = yyjson_obj_get(json, "jumpImpulse"))) { player->jumpImpulse = (float_t)yyjson_get_num(v); } + if((v = yyjson_obj_get(json, "turnSpeed"))) { + player->turnSpeed = (float_t)yyjson_get_num(v); + } errorOk(); } diff --git a/src/duskrpg/entity/component/overworld/entityplayer.h b/src/duskrpg/entity/component/overworld/entityplayer.h index 6ae34ac4..fa0b12dd 100644 --- a/src/duskrpg/entity/component/overworld/entityplayer.h +++ b/src/duskrpg/entity/component/overworld/entityplayer.h @@ -13,11 +13,14 @@ typedef struct { float_t moveSpeed; float_t jumpImpulse; + + /** Yaw turn rate, in radians/second, when facing the movement direction. */ + float_t turnSpeed; } entityplayer_t; /** - * Initializes the player component: sets default move speed and jump - * impulse. + * Initializes the player component: sets default move speed, jump + * impulse, and turn speed. * * @param mgr The entity manager that owns the entity. * @param entityId The entity ID. @@ -53,7 +56,8 @@ entityplayer_t *entityPlayerGet( * axes. No-op if the entity has no physics component. * * Also yaws the entity's position component (if any) to face the - * direction of movement. Facing is only updated while there's actual + * direction of movement, turning at most turnSpeed radians/second rather + * than snapping instantly. Facing is only updated while there's actual * movement input -- releasing input leaves the entity facing whichever * way it last moved, rather than snapping to a default orientation. * @@ -72,8 +76,8 @@ void entityPlayerUpdate( ); /** - * Serializes the player's "moveSpeed" and "jumpImpulse" into the given - * JSON object. + * Serializes the player's "moveSpeed", "jumpImpulse", and "turnSpeed" + * into the given JSON object. * * @param mgr The entity manager that owns the entity. * @param entityId The entity ID. @@ -91,9 +95,9 @@ errorret_t entityPlayerSerialize( ); /** - * Reads "moveSpeed" and "jumpImpulse" (see entityPlayerSerialize()) from - * the given JSON object and applies whichever are present, leaving the - * rest at their current values. + * Reads "moveSpeed", "jumpImpulse", and "turnSpeed" (see + * entityPlayerSerialize()) from the given JSON object and applies + * whichever are present, leaving the rest at their current values. * * @param mgr The entity manager that owns the entity. * @param entityId The entity ID. diff --git a/src/duskrpg/entity/gameprefablist.h b/src/duskrpg/entity/gameprefablist.h index 883870d5..91981289 100644 --- a/src/duskrpg/entity/gameprefablist.h +++ b/src/duskrpg/entity/gameprefablist.h @@ -8,9 +8,11 @@ // Game-specific prefabs, appended after the engine's inbuilt prefabs in // entity/entityprefablist.h. #include "entity/prefab/entityprefabplayer.h" +#include "entity/prefab/entityprefaboverworldcamera.h" // Name (Uppercase) // Extends (name of another prefab to apply first, or "" for none) // Apply function X(PLAYER, "", entityPrefabPlayerApply) +X(OVERWORLD_CAMERA, "", entityPrefabOverworldCameraApply) diff --git a/src/duskrpg/entity/prefab/CMakeLists.txt b/src/duskrpg/entity/prefab/CMakeLists.txt index f06b795f..cc17937c 100644 --- a/src/duskrpg/entity/prefab/CMakeLists.txt +++ b/src/duskrpg/entity/prefab/CMakeLists.txt @@ -6,4 +6,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC entityprefabplayer.c + entityprefaboverworldcamera.c ) diff --git a/src/duskrpg/entity/prefab/entityprefaboverworldcamera.c b/src/duskrpg/entity/prefab/entityprefaboverworldcamera.c new file mode 100644 index 00000000..6a70f9a0 --- /dev/null +++ b/src/duskrpg/entity/prefab/entityprefaboverworldcamera.c @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "entityprefaboverworldcamera.h" +#include "entity/entitymanager.h" +#include "entity/component/display/entityposition.h" +#include "entity/component/display/entitycamera.h" + +errorret_t entityPrefabOverworldCameraApply( + entitymanager_t *mgr, + const entityid_t entityId +) { + componentid_t posComp = entityAddComponent( + mgr, entityId, COMPONENT_TYPE_POSITION + ); + vec3 eye = { 10.0f, 10.0f, 10.0f }; + vec3 target = { 0.0f, 0.0f, 0.0f }; + vec3 up = { 0.0f, 1.0f, 0.0f }; + entityPositionLookAt(mgr, entityId, posComp, eye, target, up); + + componentid_t camComp = entityAddComponent( + mgr, entityId, COMPONENT_TYPE_CAMERA + ); + entitycamera_t *cam = componentGetData( + mgr, entityId, camComp, COMPONENT_TYPE_CAMERA + ); + cam->projType = ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE; + cam->nearClip = 0.1f; + cam->farClip = 1000.0f; + cam->perspective.fov = 0.9f; + + errorOk(); +} diff --git a/src/duskrpg/entity/prefab/entityprefaboverworldcamera.h b/src/duskrpg/entity/prefab/entityprefaboverworldcamera.h new file mode 100644 index 00000000..d79b24d0 --- /dev/null +++ b/src/duskrpg/entity/prefab/entityprefaboverworldcamera.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "entity/entitybase.h" +#include "error/error.h" + +/** + * Applies the OVERWORLD_CAMERA prefab: a POSITION at (10, 10, 10) looking + * at the world origin, and a perspective CAMERA (0.1/1000 near/far clip, + * 0.9 fov). C-coded equivalent of the hand-authored "camera" entity + * previously in assets/scenes/test.json. + * + * @param mgr The entity manager that owns the entity. + * @param entityId The ID of the entity to apply the prefab to. + * @return Error state. + */ +errorret_t entityPrefabOverworldCameraApply( + entitymanager_t *mgr, + const entityid_t entityId +); diff --git a/src/duskrpg/scene/CMakeLists.txt b/src/duskrpg/scene/CMakeLists.txt index 06f09ffa..40e0832b 100644 --- a/src/duskrpg/scene/CMakeLists.txt +++ b/src/duskrpg/scene/CMakeLists.txt @@ -7,3 +7,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC overworldscene.c ) + +add_subdirectory(prefab) diff --git a/src/duskrpg/scene/gameprefablist.h b/src/duskrpg/scene/gameprefablist.h new file mode 100644 index 00000000..5117ab62 --- /dev/null +++ b/src/duskrpg/scene/gameprefablist.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +// Game-specific scene prefabs, appended after the engine's inbuilt scene +// prefabs in scene/sceneprefablist.h. +#include "scene/prefab/sceneprefaboverworldscene.h" + +// Name (Uppercase) +// Extends (name of another prefab to apply first, or "" for none) +// Apply function + +X(OVERWORLDSCENE, "", scenePrefabOverworldSceneApply) diff --git a/src/duskrpg/scene/prefab/CMakeLists.txt b/src/duskrpg/scene/prefab/CMakeLists.txt new file mode 100644 index 00000000..0c4bb50e --- /dev/null +++ b/src/duskrpg/scene/prefab/CMakeLists.txt @@ -0,0 +1,9 @@ +# 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 + sceneprefaboverworldscene.c +) diff --git a/src/duskrpg/scene/prefab/sceneprefaboverworldscene.c b/src/duskrpg/scene/prefab/sceneprefaboverworldscene.c new file mode 100644 index 00000000..6b2d3052 --- /dev/null +++ b/src/duskrpg/scene/prefab/sceneprefaboverworldscene.c @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "sceneprefaboverworldscene.h" +#include "scene/scene.h" +#include "entity/entityprefab.h" + +errorret_t scenePrefabOverworldSceneApply(const sceneid_t sceneId) { + entitymanager_t *mgr = sceneGetEntities(sceneId); + + entityid_t cameraEntity = entityManagerAdd(mgr); + entitySetName(mgr, cameraEntity, "camera"); + errorChain( + entityPrefabResolveAndApply(mgr, cameraEntity, "OVERWORLD_CAMERA") + ); + + entityid_t playerEntity = entityManagerAdd(mgr); + entitySetName(mgr, playerEntity, "player"); + errorChain(entityPrefabResolveAndApply(mgr, playerEntity, "PLAYER")); + + errorOk(); +} diff --git a/src/duskrpg/scene/prefab/sceneprefaboverworldscene.h b/src/duskrpg/scene/prefab/sceneprefaboverworldscene.h new file mode 100644 index 00000000..af326c87 --- /dev/null +++ b/src/duskrpg/scene/prefab/sceneprefaboverworldscene.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "scene/scenebase.h" +#include "error/error.h" + +/** + * Applies the OVERWORLDSCENE prefab: spawns a "camera" entity (see + * entityPrefabOverworldCameraApply()) and a "player" entity (see + * entityPrefabPlayerApply()) into the scene. + * + * @param sceneId The ID of the scene to apply the prefab to. + * @return Error state. + */ +errorret_t scenePrefabOverworldSceneApply(const sceneid_t sceneId);