diff --git a/cmake/modules/Findjerryscript.cmake b/cmake/modules/Findjerryscript.cmake deleted file mode 100644 index c1f8161f..00000000 --- a/cmake/modules/Findjerryscript.cmake +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Turn things off we don't need -set(JERRY_CMDLINE OFF CACHE BOOL "" FORCE) -set(JERRY_EXT ON CACHE BOOL "" FORCE) -set(JERRY_DEBUGGER OFF CACHE BOOL "" FORCE) -set(JERRY_BUILTIN_DATE OFF CACHE BOOL "" FORCE) -set(ENABLE_LTO OFF CACHE BOOL "" FORCE) - -# Fetch Jerry -include(FetchContent) -FetchContent_Declare( - jerryscript - GIT_REPOSITORY https://git.wish.moe/YourWishes/jerryscript - GIT_TAG float32-fix -) -FetchContent_MakeAvailable(jerryscript) - -# Mark found -set(jerryscript_FOUND ON) - -# Define targets -if(TARGET jerryscript-core) - set(JERRY_CORE_TARGET jerryscript-core) -elseif(TARGET jerry-core) - set(JERRY_CORE_TARGET jerry-core) -endif() - -if(TARGET jerryscript-ext) - set(JERRY_EXT_TARGET jerryscript-ext) -elseif(TARGET jerry-ext) - set(JERRY_EXT_TARGET jerry-ext) -endif() - -if(TARGET jerryscript-port-default) - set(JERRY_PORT_TARGET jerryscript-port-default) -elseif(TARGET jerry-port-default) - set(JERRY_PORT_TARGET jerry-port-default) -elseif(TARGET jerryscript-port) - set(JERRY_PORT_TARGET jerryscript-port) -elseif(TARGET jerry-port) - set(JERRY_PORT_TARGET jerry-port) -endif() - -if(NOT JERRY_CORE_TARGET) - message(FATAL_ERROR "JerryScript core target not found") -endif() - -if(NOT JERRY_EXT_TARGET) - message(FATAL_ERROR "JerryScript ext target not found") -endif() - -if(NOT JERRY_PORT_TARGET) - message(FATAL_ERROR "JerryScript port target not found") -endif() - -foreach(tgt IN ITEMS - ${JERRY_CORE_TARGET} - ${JERRY_EXT_TARGET} - ${JERRY_PORT_TARGET} -) - if(TARGET ${tgt}) - set_property(TARGET ${tgt} PROPERTY INTERPROCEDURAL_OPTIMIZATION OFF) - target_compile_definitions(${JERRY_CORE_TARGET} PRIVATE - JERRY_NUMBER_TYPE_FLOAT64=0 - JERRY_BUILTIN_DATE=0 - ) - endif() -endforeach() - -# Export include dirs through the targets -target_include_directories(${JERRY_CORE_TARGET} INTERFACE - ${jerryscript_SOURCE_DIR}/jerry-core/include -) - -target_include_directories(${JERRY_EXT_TARGET} INTERFACE - ${jerryscript_SOURCE_DIR}/jerry-ext/include -) - -target_include_directories(${JERRY_PORT_TARGET} INTERFACE - ${jerryscript_SOURCE_DIR}/jerry-port/default/include -) - -# Suppress JerryScript-only warning -if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(${JERRY_CORE_TARGET} PRIVATE - -Wno-error - ) -endif() - -add_library(jerryscript::core ALIAS ${JERRY_CORE_TARGET}) -add_library(jerryscript::ext ALIAS ${JERRY_EXT_TARGET}) -add_library(jerryscript::port ALIAS ${JERRY_PORT_TARGET}) diff --git a/cmake/modules/duskjs2c.cmake b/cmake/modules/duskjs2c.cmake deleted file mode 100644 index dada53df..00000000 --- a/cmake/modules/duskjs2c.cmake +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# dusk_embed_js(TARGET JS_FILE [NAME identifier]) -# -# Converts a JS file into a C string header in DUSK_GENERATED_HEADERS_DIR. -# The generated header defines: -# static const char [] = "..."; -# static const size_t _SIZE = sizeof() - 1; -# -# NAME defaults to the uppercase stem + "_JS" (e.g. scene.js -> SCENE_JS). -function(dusk_embed_js TARGET JS_FILE) - cmake_parse_arguments(ARG "" "NAME" "" ${ARGN}) - - get_filename_component(JS_ABS "${JS_FILE}" ABSOLUTE) - get_filename_component(JS_STEM "${JS_FILE}" NAME_WE) - - set(OUTPUT_HEADER "${DUSK_GENERATED_HEADERS_DIR}/${JS_STEM}_js.h") - - set(NAME_ARG "") - if(ARG_NAME) - set(NAME_ARG "--name" "${ARG_NAME}") - endif() - - add_custom_command( - OUTPUT "${OUTPUT_HEADER}" - COMMAND ${Python3_EXECUTABLE} -m tools.js2c - --input "${JS_ABS}" - --output "${OUTPUT_HEADER}" - ${NAME_ARG} - WORKING_DIRECTORY "${DUSK_ROOT_DIR}" - DEPENDS "${JS_ABS}" - COMMENT "js2c: ${JS_STEM}.js -> ${JS_STEM}_js.h" - VERBATIM - ) - - file(RELATIVE_PATH JS_REL "${DUSK_ROOT_DIR}" "${JS_ABS}") - string(MAKE_C_IDENTIFIER "dusk_js2c_${JS_REL}" JS_TARGET) - add_custom_target(${JS_TARGET} DEPENDS "${OUTPUT_HEADER}") - add_dependencies(${TARGET} ${JS_TARGET}) -endfunction() diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index 141f7256..a39c2f48 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -32,15 +32,6 @@ if(NOT yyjson_FOUND) endif() endif() -if(NOT jerryscript_FOUND) - find_package(jerryscript REQUIRED) - target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - jerryscript::core - jerryscript::ext - jerryscript::port - ) -endif() - if(DUSK_BACKTRACE) target_link_options(${DUSK_LIBRARY_TARGET_NAME} PUBLIC -rdynamic) target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC @@ -66,24 +57,19 @@ add_subdirectory(event) add_subdirectory(assert) add_subdirectory(asset) add_subdirectory(cutscene) -add_subdirectory(item) -add_subdirectory(story) add_subdirectory(console) add_subdirectory(display) add_subdirectory(log) add_subdirectory(engine) -add_subdirectory(entity) add_subdirectory(error) add_subdirectory(input) add_subdirectory(locale) -add_subdirectory(physics) +add_subdirectory(rpg) add_subdirectory(scene) -add_subdirectory(script) add_subdirectory(system) add_subdirectory(time) add_subdirectory(ui) add_subdirectory(network) -add_subdirectory(overworld) add_subdirectory(save) add_subdirectory(util) add_subdirectory(thread) \ No newline at end of file diff --git a/src/dusk/asset/asset.c b/src/dusk/asset/asset.c index c60f8c80..ee37834d 100644 --- a/src/dusk/asset/asset.c +++ b/src/dusk/asset/asset.c @@ -411,21 +411,6 @@ errorret_t assetDispose(void) { assertIsMainThread("Must be called from the main thread."); threadStop(&ASSET.loadThread); - // Free any script read-buffers left behind by an in-flight async load - // that was interrupted before the sync eval phase ran. - for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - assetloading_t *loading = &ASSET.loading[i]; - if( - loading->entry != NULL && - loading->type == ASSET_LOADER_TYPE_SCRIPT && - loading->loading.script.buffer != NULL - ) { - memoryFree(loading->loading.script.buffer); - loading->loading.script.buffer = NULL; - } - threadMutexDispose(&loading->mutex); - } - // Dispose every non-null entry so type-specific dispose callbacks // (e.g. assetScriptDispose freeing jerry values) run before the // scripting engine is torn down. diff --git a/src/dusk/asset/loader/CMakeLists.txt b/src/dusk/asset/loader/CMakeLists.txt index 21f5928c..9bca3019 100644 --- a/src/dusk/asset/loader/CMakeLists.txt +++ b/src/dusk/asset/loader/CMakeLists.txt @@ -14,5 +14,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} # Subdirs add_subdirectory(display) add_subdirectory(locale) -add_subdirectory(json) -add_subdirectory(script) \ No newline at end of file +add_subdirectory(json) \ No newline at end of file diff --git a/src/dusk/asset/loader/assetloader.c b/src/dusk/asset/loader/assetloader.c index bce09b0e..5987246f 100644 --- a/src/dusk/asset/loader/assetloader.c +++ b/src/dusk/asset/loader/assetloader.c @@ -39,10 +39,4 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = { .loadAsync = assetJsonLoaderAsync, .dispose = assetJsonDispose }, - - [ASSET_LOADER_TYPE_SCRIPT] = { - .loadSync = assetScriptLoaderSync, - .loadAsync = assetScriptLoaderAsync, - .dispose = assetScriptDispose - }, }; diff --git a/src/dusk/asset/loader/assetloader.h b/src/dusk/asset/loader/assetloader.h index 14e983bb..8324581d 100644 --- a/src/dusk/asset/loader/assetloader.h +++ b/src/dusk/asset/loader/assetloader.h @@ -11,7 +11,6 @@ #include "asset/loader/display/assettilesetloader.h" #include "asset/loader/locale/assetlocaleloader.h" #include "asset/loader/json/assetjsonloader.h" -#include "asset/loader/script/assetscriptloader.h" typedef enum { ASSET_LOADER_TYPE_NULL, @@ -21,7 +20,6 @@ typedef enum { ASSET_LOADER_TYPE_TILESET, ASSET_LOADER_TYPE_LOCALE, ASSET_LOADER_TYPE_JSON, - ASSET_LOADER_TYPE_SCRIPT, ASSET_LOADER_TYPE_COUNT } assetloadertype_t; @@ -32,7 +30,6 @@ typedef union { assettilesetloaderinput_t tileset; assetlocaleloaderinput_t locale; assetjsonloaderinput_t json; - assetscriptloaderinput_t script; } assetloaderinput_t; typedef union { @@ -41,7 +38,6 @@ typedef union { assettilesetloaderloading_t tileset; assetlocaleloaderloading_t locale; assetjsonloaderloading_t json; - assetscriptloaderloading_t script; } assetloaderloading_t; typedef union { @@ -50,7 +46,6 @@ typedef union { assettilesetoutput_t tileset; assetlocaleoutput_t locale; assetjsonoutput_t json; - assetscriptoutput_t script; } assetloaderoutput_t; typedef struct assetloading_s assetloading_t; diff --git a/src/dusk/asset/loader/script/CMakeLists.txt b/src/dusk/asset/loader/script/CMakeLists.txt deleted file mode 100644 index 76d9bd92..00000000 --- a/src/dusk/asset/loader/script/CMakeLists.txt +++ /dev/null @@ -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 - assetscriptloader.c -) diff --git a/src/dusk/asset/loader/script/assetscriptloader.c b/src/dusk/asset/loader/script/assetscriptloader.c deleted file mode 100644 index eafe190e..00000000 --- a/src/dusk/asset/loader/script/assetscriptloader.c +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetscriptloader.h" -#include "asset/loader/assetloading.h" -#include "asset/loader/assetentry.h" -#include "asset/loader/assetloader.h" -#include "script/module/require/modulerequire.h" -#include "util/memory.h" -#include "assert/assert.h" -#include - -errorret_t assetScriptLoaderAsync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertNotMainThread("Async loader should not be on main thread."); - - if(loading->loading.script.state != ASSET_SCRIPT_LOADING_STATE_READ_FILE) { - errorOk(); - } - - assertNull(loading->loading.script.buffer, "Buffer already defined?"); - - assetfile_t *file = &loading->loading.script.file; - assetLoaderErrorChain( - loading, assetFileInit(file, loading->entry->name, NULL, NULL) - ); - - uint8_t *buffer = NULL; - size_t size = 0; - assetLoaderErrorChain(loading, assetFileReadEntire(file, &buffer, &size)); - assetLoaderErrorChain(loading, assetFileDispose(file)); - - // Null-terminate for jerry_eval. - memoryResize((void **)&buffer, size, size + 1); - buffer[size] = '\0'; - - loading->loading.script.buffer = buffer; - loading->loading.script.size = size; - loading->loading.script.state = ASSET_SCRIPT_LOADING_STATE_EXEC; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetScriptLoaderSync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_SCRIPT, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - switch(loading->loading.script.state) { - case ASSET_SCRIPT_LOADING_STATE_INITIAL: - loading->loading.script.state = ASSET_SCRIPT_LOADING_STATE_READ_FILE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; - errorOk(); - break; - - case ASSET_SCRIPT_LOADING_STATE_EXEC: - break; - - default: - errorOk(); - } - - // Get read buffer - uint8_t *buffer = loading->loading.script.buffer; - assertNotNull(buffer, "Script buffer should have been loaded by now."); - - // Get the current global script realm - jerry_value_t global = jerry_current_realm(); - - // Replace globalThis.module with a new `module = {}` - jerry_value_t oldModule = jerry_object_get_sz(global, "module"); - - jerry_value_t module = jerry_object(); - jerry_object_set_sz(global, "module", module); - - // Eval the script, we handle failure later down the code. - jerry_value_t result = jerry_eval( - buffer, - loading->loading.script.size, - JERRY_PARSE_NO_OPTS - ); - - // Free the read buffer - memoryFree(buffer); - loading->loading.script.buffer = NULL; - - // Restore globalThis.module - jerry_object_set_sz(global, "module", oldModule); - jerry_value_free(oldModule); - jerry_value_free(global); - - if(jerry_value_is_exception(result)) { - jerry_value_free(module); - - loading->entry->data.script.exports = jerry_undefined(); - loading->entry->state = ASSET_ENTRY_STATE_ERROR; - - // Get error string - char_t buf[256]; - moduleBaseExceptionMessage(result, buf, sizeof(buf)); - jerry_value_free(result); - assetLoaderErrorThrow( - loading, - "Script execution failed: %s: %s", loading->entry->name, buf - ); - } - - // Get module.exports - jerry_value_t exports = jerry_object_get_sz(module, "exports"); - jerry_value_free(result); - jerry_value_free(module); - - // Store the exports. - loading->entry->data.script.exports = exports; - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -errorret_t assetScriptDispose(assetentry_t *entry) { - assertNotNull(entry, "Asset entry cannot be NULL"); - assertTrue(entry->type == ASSET_LOADER_TYPE_SCRIPT, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - if( - entry->data.script.exports != 0 && - !jerry_value_is_undefined(entry->data.script.exports) - ) { - jerry_value_free(entry->data.script.exports); - entry->data.script.exports = 0; - } - - errorOk(); -} diff --git a/src/dusk/asset/loader/script/assetscriptloader.h b/src/dusk/asset/loader/script/assetscriptloader.h deleted file mode 100644 index b38362bf..00000000 --- a/src/dusk/asset/loader/script/assetscriptloader.h +++ /dev/null @@ -1,60 +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 "script/scriptmodule.h" - -#define ASSET_SCRIPT_CHUNK_SIZE 1024 - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; - -typedef struct { - void *nothing; -} assetscriptloaderinput_t; - -typedef scriptmodule_t assetscriptoutput_t; - -typedef enum { - ASSET_SCRIPT_LOADING_STATE_INITIAL, - ASSET_SCRIPT_LOADING_STATE_READ_FILE, - ASSET_SCRIPT_LOADING_STATE_EXEC, - ASSET_SCRIPT_LOADING_STATE_DONE -} assetscriptloadingstate_t; - -typedef struct { - assetfile_t file; - assetscriptloadingstate_t state; - uint8_t *buffer; - size_t size; -} assetscriptloaderloading_t; - -/** - * Asynchronous loader for a script asset/module. - * - * @param loading The loading context. - * @returns An error code and state. - */ -errorret_t assetScriptLoaderAsync(assetloading_t *loading); - -/** - * Synchronous loader for a script asset/module. This executes the script after - * it has been loaded by the async loader. - * - * @param loading The loading context. - * @returns An error code and state. - */ -errorret_t assetScriptLoaderSync(assetloading_t *loading); - -/** - * Disposes of a loaded script asset/module. - * - * @param entry The asset entry to dispose. - * @returns An error code and state. - */ -errorret_t assetScriptDispose(assetentry_t *entry); diff --git a/src/dusk/display/display.c b/src/dusk/display/display.c index 49d3cbc5..4e514306 100644 --- a/src/dusk/display/display.c +++ b/src/dusk/display/display.c @@ -38,6 +38,10 @@ errorret_t displayInit(void) { &TEXTURE_WHITE, 4, 4, TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_WHITE_PIXELS } )); + errorChain(textureInit( + &TEXTURE_TEST, 4, 4, + TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_TEST_PIXELS } + )); // Standard meshes errorChain(quadInit()); @@ -100,6 +104,8 @@ errorret_t displayDispose(void) { errorChain(spriteBatchDispose()); screenDispose(); errorChain(textDispose()); + errorChain(textureDispose(&TEXTURE_WHITE)); + errorChain(textureDispose(&TEXTURE_TEST)); #ifdef displayPlatformDispose displayPlatformDispose(); diff --git a/src/dusk/display/text/text.c b/src/dusk/display/text/text.c index e7878df7..2b16229c 100644 --- a/src/dusk/display/text/text.c +++ b/src/dusk/display/text/text.c @@ -93,15 +93,6 @@ errorret_t textDraw( float_t posX = x; float_t posY = y; - errorChain(shaderSetTexture( - &SHADER_UNLIT, SHADER_UNLIT_TEXTURE, font->texture - )); - - #if MESH_ENABLE_COLOR - #else - errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, color)); - #endif - char_t c; int32_t i = 0; while((c = text[i++]) != '\0') { diff --git a/src/dusk/display/texture/texture.c b/src/dusk/display/texture/texture.c index 0e7268bc..5d911b42 100644 --- a/src/dusk/display/texture/texture.c +++ b/src/dusk/display/texture/texture.c @@ -19,6 +19,14 @@ color_t TEXTURE_WHITE_PIXELS[4*4] = { COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, }; +texture_t TEXTURE_TEST; +color_t TEXTURE_TEST_PIXELS[4*4] = { + COLOR_BLACK, COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA, + COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA, COLOR_BLACK, + COLOR_BLACK, COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA, + COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA, COLOR_BLACK, +}; + errorret_t textureInit( texture_t *texture, const int32_t width, diff --git a/src/dusk/display/texture/texture.h b/src/dusk/display/texture/texture.h index e77e4e66..c94f80f0 100644 --- a/src/dusk/display/texture/texture.h +++ b/src/dusk/display/texture/texture.h @@ -30,6 +30,8 @@ typedef union texturedata_u { extern texture_t TEXTURE_WHITE; extern color_t TEXTURE_WHITE_PIXELS[4*4]; +extern texture_t TEXTURE_TEST; +extern color_t TEXTURE_TEST_PIXELS[4*4]; /** * Initializes a texture. diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index ec57a4e2..7f68dce1 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -10,6 +10,7 @@ #include "time/time.h" #include "input/input.h" #include "locale/localemanager.h" +#include "rpg/rpg.h" #include "display/display.h" #include "scene/scene.h" #include "cutscene/cutscene.h" @@ -17,14 +18,9 @@ #include "ui/ui.h" #include "ui/uitextbox.h" #include "assert/assert.h" -#include "entity/entitymanager.h" -#include "entity/component/physics/entityphysics.h" -#include "physics/physicsmanager.h" #include "network/network.h" #include "system/system.h" #include "console/console.h" -#include "script/script.h" -#include "item/backpack.h" #include "save/save.h" engine_t ENGINE; @@ -49,15 +45,12 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { errorChain(uiInit()); errorChain(uiTextboxInit()); errorChain(cutsceneInit()); - entityManagerInit(); - backpackInit(); - physicsManagerInit(); + errorChain(rpgInit()); errorChain(networkInit()); - errorChain(scriptInit()); errorChain(sceneInit()); consolePrint("Engine initialized"); - errorChain(scriptExecFile("init.js")); + sceneSet(SCENE_TYPE_OVERWORLD); errorOk(); } @@ -68,16 +61,15 @@ errorret_t engineUpdate(void) { timeUpdate(); inputUpdate(); consoleUpdate(); - entityManagerUpdate(); + errorChain(rpgUpdate()); uiUpdate(); errorChain(uiTextboxUpdate()); - physicsManagerUpdate(); - errorChain(displayUpdate()); errorChain(cutsceneUpdate()); errorChain(sceneUpdate()); errorChain(assetUpdate()); - errorChain(scriptUpdate()); + // Render + errorChain(displayUpdate()); if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false; errorOk(); } @@ -90,9 +82,8 @@ errorret_t engineDispose(void) { uiTextboxDispose(); cutsceneDispose(); errorChain(sceneDispose()); - errorChain(scriptDispose()); errorChain(networkDispose()); - entityManagerDispose(); + rpgDispose(); localeManagerDispose(); uiDispose(); consoleDispose(); diff --git a/src/dusk/entity/component.c b/src/dusk/entity/component.c deleted file mode 100644 index ad23b598..00000000 --- a/src/dusk/entity/component.c +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entitymanager.h" -#include "assert/assert.h" -#include "util/memory.h" - -componentdefinition_t COMPONENT_DEFINITIONS[] = { - [COMPONENT_TYPE_NULL] = { 0 }, - - #define X(enm, type, field, iMethod, dMethod, rMethod) \ - [COMPONENT_TYPE_##enm] = { \ - .enumName = #enm, \ - .name = #field, \ - .init = iMethod, \ - .dispose = dMethod, \ - .render = rMethod \ - }, - - #include "componentlist.h" - #undef X - - [COMPONENT_TYPE_COUNT] = { 0 } -}; - -void componentInit( - const entityid_t entityId, - const componentid_t componentId, - const componenttype_t type -) { - assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB"); - assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB"); - assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB"); - assertTrue(type != COMPONENT_TYPE_NULL, "Cannot initialize null component"); - - componentindex_t index = componentGetIndex(entityId, componentId); - component_t *cmp = &ENTITY_MANAGER.components[index]; - memoryZero(cmp, sizeof(component_t)); - - cmp->type = type; - if(COMPONENT_DEFINITIONS[type].init) { - COMPONENT_DEFINITIONS[type].init(entityId, componentId); - } -} - -void * componentGetData( - const entityid_t entityId, - const componentid_t componentId, - const componenttype_t type -) { - assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB"); - assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB"); - assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB"); - assertTrue(type != COMPONENT_TYPE_NULL, "Cannot get data of null component"); - - componentindex_t index = componentGetIndex(entityId, componentId); - component_t *cmp = &ENTITY_MANAGER.components[index]; - assertTrue(cmp->type == type, "Component type mismatch"); - - return &cmp->data; -} - -componentindex_t componentGetIndex( - const entityid_t entityId, - const componentid_t componentId -) { - assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB"); - assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB"); - return (entityId * ENTITY_COMPONENT_COUNT_MAX) + componentId; -} - -entityid_t componentGetEntitiesWithComponent( - const componenttype_t type, - entityid_t outEntities[ENTITY_COUNT_MAX], - componentid_t outComponents[ENTITY_COUNT_MAX] -) { - assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB"); - assertTrue(type != COMPONENT_TYPE_NULL, "Cannot check NULL type"); - assertNotNull(outEntities, "Output entities array cannot be null"); - assertNotNull(outComponents, "Output components array cannot be null"); - - entityid_t written = 0; - for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) { - componentid_t used = ENTITY_MANAGER.entitiesWithComponent[ - type * ENTITY_COUNT_MAX + i - ]; - if(used == COMPONENT_ID_INVALID) continue; - assertTrue( - ENTITY_MANAGER.components[componentGetIndex(i, used)].type == type, - "Component type mismatch in entitiesWithComponent lookup" - ); - assertTrue( - (ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) != 0, - "Inactive entity in entitiesWithComponent lookup" - ); - assertTrue( - used < ENTITY_COMPONENT_COUNT_MAX, - "Component ID OOB in entitiesWithComponent lookup" - ); - assertTrue( - componentGetIndex(i,used) < ENTITY_COUNT_MAX*ENTITY_COMPONENT_COUNT_MAX, - "Component index OOB in entitiesWithComponent lookup" - ); - assertTrue( - ENTITY_MANAGER.components[componentGetIndex(i,used)].type == type, - "Component type mismatch in entitiesWithComponent lookup" - ); - outComponents[written] = used; - outEntities[written++] = i; - } - return written; -} - -errorret_t componentRenderAll(void) { - for(entityid_t eid = 0; eid < ENTITY_COUNT_MAX; eid++) { - if(!(ENTITY_MANAGER.entities[eid].state & ENTITY_STATE_ACTIVE)) continue; - for(componentid_t cid = 0; cid < ENTITY_COMPONENT_COUNT_MAX; cid++) { - component_t *cmp = &ENTITY_MANAGER.components[ - componentGetIndex(eid, cid) - ]; - if(cmp->type == COMPONENT_TYPE_NULL) continue; - if(!COMPONENT_DEFINITIONS[cmp->type].render) continue; - errorChain(COMPONENT_DEFINITIONS[cmp->type].render(eid, cid)); - } - } - errorOk(); -} - -void componentDispose( - const entityid_t entityId, - const componentid_t componentId -) { - assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB"); - assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB"); - - componentindex_t index = componentGetIndex(entityId, componentId); - component_t *cmp = &ENTITY_MANAGER.components[index]; - if(cmp->type == COMPONENT_TYPE_NULL) return; - - if(COMPONENT_DEFINITIONS[cmp->type].dispose) { - COMPONENT_DEFINITIONS[cmp->type].dispose(entityId, componentId); - } - - cmp->type = COMPONENT_TYPE_NULL; -} \ No newline at end of file diff --git a/src/dusk/entity/component.h b/src/dusk/entity/component.h deleted file mode 100644 index c723ec02..00000000 --- a/src/dusk/entity/component.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entitybase.h" - -#define X(enumName, type, field, init, dispose, render) \ - // do nothing -#include "componentlist.h" -#undef X - -typedef union { - #define X(enumName, type, field, init, dispose, render) type field; - #include "componentlist.h" - #undef X -} componentdata_t; - -typedef struct { - const char_t *enumName; - const char_t *name; - void (*init)(const entityid_t, const componentid_t); - void (*dispose)(const entityid_t, const componentid_t); - errorret_t (*render)(const entityid_t, const componentid_t); -} componentdefinition_t; - -typedef enum { - COMPONENT_TYPE_NULL, - - #define X(enumName, type, field, init, dispose, render) \ - COMPONENT_TYPE_##enumName, - #include "componentlist.h" - #undef X - - COMPONENT_TYPE_COUNT -} componenttype_t; - -typedef struct { - componenttype_t type; - componentdata_t data; -} component_t; - -extern componentdefinition_t COMPONENT_DEFINITIONS[]; - -/** - * Initializes a component of the given type for the entity with component ID. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param type The type of the component to initialize. - */ -void componentInit( - const entityid_t entityId, - const componentid_t componentId, - const componenttype_t type -); - -/** - * Gets the pointer to the data of a component for the entity with component ID. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param type The type of the component to get, only used for assertion. - * @return A pointer to the component data. - */ -void * componentGetData( - const entityid_t entityId, - const componentid_t componentId, - const componenttype_t type -); - -/** - * Gets the index of a component for the entity with component ID. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @return The index of the component in the component array. - */ -componentindex_t componentGetIndex( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Gets the entity IDs of all entities with a component of the given type. - * - * @param type The type of the component to get entities for. - * @param outEntities An array to write the entity IDs to, must be at least - * ENTITY_COUNT_MAX in size. - * @param outComponents An array to write the component IDs to. - * @return The number of entity IDs written to outEntities. - */ -entityid_t componentGetEntitiesWithComponent( - const componenttype_t type, - entityid_t outEntities[ENTITY_COUNT_MAX], - componentid_t outComponents[ENTITY_COUNT_MAX] -); - -/** - * Disposes of a component for the entity with component ID. - * - * @param entityId The entity ID. - * @param componentId The component ID. - */ -void componentDispose( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Calls the render callback on every active component that defines one. - * Iterates all active entities and all their component slots. No-op for - * components whose definition has render == NULL. - * - * @return Error state. - */ -errorret_t componentRenderAll(void); \ No newline at end of file diff --git a/src/dusk/entity/component/CMakeLists.txt b/src/dusk/entity/component/CMakeLists.txt deleted file mode 100644 index 241b685c..00000000 --- a/src/dusk/entity/component/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -add_subdirectory(display) -add_subdirectory(physics) -add_subdirectory(trigger) \ No newline at end of file diff --git a/src/dusk/entity/component/display/CMakeLists.txt b/src/dusk/entity/component/display/CMakeLists.txt deleted file mode 100644 index eeca751f..00000000 --- a/src/dusk/entity/component/display/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - entityposition.c - entitycamera.c - entityrenderable.c -) \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitycamera.c b/src/dusk/entity/component/display/entitycamera.c deleted file mode 100644 index d9a2727e..00000000 --- a/src/dusk/entity/component/display/entitycamera.c +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entity/entitymanager.h" -#include "entity/entity.h" -#include "entity/component/display/entityposition.h" -#include "display/framebuffer/framebuffer.h" -#include "display/screen/screen.h" - -void entityCameraInit(const entityid_t ent, const componentid_t comp) { - entitycamera_t *cam = (entitycamera_t *)componentGetData( - ent, comp, COMPONENT_TYPE_CAMERA - ); - cam->nearClip = 0.1f; - cam->farClip = 5000.0f; - cam->projType = ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE; - cam->perspective.fov = glm_rad(45.0f); -} - -void entityCameraGetProjection( - const entityid_t ent, - const componentid_t comp, - mat4 out -) { - entitycamera_t *cam = (entitycamera_t *)componentGetData( - ent, comp, COMPONENT_TYPE_CAMERA - ); - - if( - cam->projType == ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE || - cam->projType == ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED - ) { - glm_mat4_identity(out); - glm_perspective( - cam->perspective.fov, - SCREEN.aspect, - cam->nearClip, - cam->farClip, - out - ); - - if(cam->projType == ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED) { - out[1][1] *= -1.0f; - } - } else if(cam->projType == ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) { - glm_mat4_identity(out); - glm_ortho( - cam->orthographic.left, - cam->orthographic.right, - cam->orthographic.top, - cam->orthographic.bottom, - cam->nearClip, - cam->farClip, - out - ); - } -} - -entityid_t entityCameraGetCurrent(void) { - entityid_t camEnts[ENTITY_COUNT_MAX]; - componentid_t camComps[ENTITY_COUNT_MAX]; - entityid_t count = componentGetEntitiesWithComponent( - COMPONENT_TYPE_CAMERA, camEnts, camComps - ); - if(count == 0) return ENTITY_ID_INVALID; - return camEnts[0]; -} - -void entityCameraGetForward(const entityid_t entityId, vec2 out) { - componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION); - entityposition_t *pos = entityPositionGet(entityId, posComp); - // View matrix column layout: M[col][row], - // forward = {-M[0][2], -M[1][2], -M[2][2]} - float_t fx = -pos->worldTransform[0][2]; - float_t fz = -pos->worldTransform[2][2]; - float_t len = sqrtf(fx * fx + fz * fz); - if(len > 1e-6f) { fx /= len; fz /= len; } - out[0] = fx; - out[1] = fz; -} - -void entityCameraLookAtPixelPerfect( - const entityid_t ent, - const componentid_t posComp, - const componentid_t camComp, - const vec3 point, - const vec3 eyeOffset, - const float_t scale -) { - entitycamera_t *cam = (entitycamera_t *)componentGetData( - ent, camComp, COMPONENT_TYPE_CAMERA - ); - float_t dist = ( - (float_t)SCREEN.height / (2.0f * scale * tanf(cam->perspective.fov * 0.5f)) - ); - - vec3 eye = { - point[0] + eyeOffset[0], - point[1] + dist + eyeOffset[1], - point[2] + eyeOffset[2] - }; - vec3 up = { 0.0f, 0.0f, -1.0f }; - entityPositionLookAt(ent, posComp, eye, (float_t *)point, up); -} - -void entityCameraGetRight(const entityid_t entityId, vec2 out) { - componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION); - entityposition_t *pos = entityPositionGet(entityId, posComp); - // View matrix column layout: right = {M[0][0], M[1][0], M[2][0]} - float_t rx = pos->worldTransform[0][0]; - float_t rz = pos->worldTransform[2][0]; - float_t len = sqrtf(rx * rx + rz * rz); - if(len > 1e-6f) { rx /= len; rz /= len; } - out[0] = rx; - out[1] = rz; -} \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitycamera.h b/src/dusk/entity/component/display/entitycamera.h deleted file mode 100644 index b866e354..00000000 --- a/src/dusk/entity/component/display/entitycamera.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entity/entitybase.h" - -typedef enum { - ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE, - ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED, - ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC -} entitycameraprojectiontype_t; - -typedef struct { - union { - struct { - float_t fov; - } perspective; - - struct { - float_t left; - float_t right; - float_t top; - float_t bottom; - } orthographic; - }; - - float_t nearClip; - float_t farClip; - entitycameraprojectiontype_t projType; -} entitycamera_t; - -/** - * Initializes an entity camera component. - * - * @param ent The entity ID. - * @param comp The component ID. - */ -void entityCameraInit(const entityid_t ent, const componentid_t comp); - -/** - * Renders out the projection matrix for the given camera. - * - * @param ent The entity ID. - * @param comp The component ID. - * @param out The output projection matrix. - */ -void entityCameraGetProjection( - const entityid_t ent, - const componentid_t comp, - mat4 out -); - -/** - * Returns the entity ID of the first active camera, or ENTITY_ID_INVALID if - * none are active. - */ -entityid_t entityCameraGetCurrent(void); - -/** - * Gets the camera's horizontal forward direction (XZ plane) from its position - * component. Automatically finds the position component on the entity. - * - * @param entityId The camera entity ID. - * @param out Output vec2: {forwardX, forwardZ} normalized. - */ -void entityCameraGetForward(const entityid_t entityId, vec2 out); - -/** - * Gets the camera's horizontal right direction (XZ plane) from its position - * component. Automatically finds the position component on the entity. - * - * @param entityId The camera entity ID. - * @param out Output vec2: {rightX, rightZ} normalized. - */ -void entityCameraGetRight(const entityid_t entityId, vec2 out); - -/** - * Positions the camera to look at a 3D point at a pixel-perfect distance - * derived from the camera's FOV and screen height. - * - * @param ent The camera entity ID. - * @param posComp The position component ID. - * @param camComp The camera component ID. - * @param point World position to look at. - * @param eyeOffset Offset added to the eye position only (not the target). - * @param scale Pixels per world unit. 1.0 = pixel perfect, 2.0 = 2px per unit. - */ -void entityCameraLookAtPixelPerfect( - const entityid_t ent, - const componentid_t posComp, - const componentid_t camComp, - const vec3 point, - const vec3 eyeOffset, - const float_t scale -); \ No newline at end of file diff --git a/src/dusk/entity/component/display/entityposition.c b/src/dusk/entity/component/display/entityposition.c deleted file mode 100644 index 46857036..00000000 --- a/src/dusk/entity/component/display/entityposition.c +++ /dev/null @@ -1,630 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entity/entitymanager.h" - -// Decompose localTransform into the PRS cache. Only called when PRS_DIRTY. -static void entityPositionEnsurePRS(entityposition_t *pos) { - if(!(pos->flags & ENTITY_POSITION_FLAG_PRS_DIRTY)) return; - entityPositionDecompose(pos); - pos->flags &= ~ENTITY_POSITION_FLAG_PRS_DIRTY; -} - -// Rebuild localTransform from the PRS cache. Only rebuilds what changed. -static void entityPositionEnsureLocal(entityposition_t *pos) { - const uint8_t dirty = pos->flags & ( - ENTITY_POSITION_FLAG_ROTATION_DIRTY | ENTITY_POSITION_FLAG_POSITION_DIRTY - ); - if(!dirty) return; - - if(dirty & ENTITY_POSITION_FLAG_ROTATION_DIRTY) { - // Rotation or scale changed: rebuild cols 0-2 analytically (XYZ euler). - const float_t c0 = cosf(pos->rotation[0]), s0 = sinf(pos->rotation[0]); - const float_t c1 = cosf(pos->rotation[1]), s1 = sinf(pos->rotation[1]); - const float_t c2 = cosf(pos->rotation[2]), s2 = sinf(pos->rotation[2]); - const float_t s0s1 = s0 * s1; - const float_t c0s1 = c0 * s1; - - pos->localTransform[0][0] = c1 * c2 * pos->scale[0]; - pos->localTransform[0][1] = (c0 * s2 + s0s1 * c2) * pos->scale[0]; - pos->localTransform[0][2] = (s0 * s2 - c0s1 * c2) * pos->scale[0]; - pos->localTransform[0][3] = 0.0f; - - pos->localTransform[1][0] = -c1 * s2 * pos->scale[1]; - pos->localTransform[1][1] = (c0 * c2 - s0s1 * s2) * pos->scale[1]; - pos->localTransform[1][2] = (s0 * c2 + c0s1 * s2) * pos->scale[1]; - pos->localTransform[1][3] = 0.0f; - - pos->localTransform[2][0] = s1 * pos->scale[2]; - pos->localTransform[2][1] = -s0 * c1 * pos->scale[2]; - pos->localTransform[2][2] = c0 * c1 * pos->scale[2]; - pos->localTransform[2][3] = 0.0f; - } - - if(dirty & ENTITY_POSITION_FLAG_POSITION_DIRTY) { - // Only position changed: update column 3 only (no trig needed). - pos->localTransform[3][0] = pos->position[0]; - pos->localTransform[3][1] = pos->position[1]; - pos->localTransform[3][2] = pos->position[2]; - pos->localTransform[3][3] = 1.0f; - } - - pos->flags &= ~( - ENTITY_POSITION_FLAG_ROTATION_DIRTY | ENTITY_POSITION_FLAG_POSITION_DIRTY - ); -} - -// Recompute worldTransform from the parent chain. Only called when WORLD_DIRTY. -static void entityPositionEnsureWorld(entityposition_t *pos) { - if(!(pos->flags & ENTITY_POSITION_FLAG_WORLD_DIRTY)) return; - entityPositionEnsureLocal(pos); - - if(pos->parentEntityId != ENTITY_ID_INVALID) { - // Parented: world = parent.world x local. worldTransform must be written - // because children (and this node's getters) read it. - entityposition_t *parent = componentGetData( - pos->parentEntityId, pos->parentComponentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsureWorld(parent); - glm_mat4_mul( - parent->worldTransform, pos->localTransform, pos->worldTransform - ); - } else if(pos->childCount > 0) { - // Parentless root with children: children need a valid worldTransform to - // multiply against, but world == local, so just copy. - glm_mat4_copy(pos->localTransform, pos->worldTransform); - } - // Parentless leaf: world == local. Getters read localTransform directly; - // no copy needed. - - pos->flags &= ~ENTITY_POSITION_FLAG_WORLD_DIRTY; -} - -void entityPositionMarkDirty(entityposition_t *pos) { - if(pos->flags & ENTITY_POSITION_FLAG_WORLD_DIRTY) return; - pos->flags |= ENTITY_POSITION_FLAG_WORLD_DIRTY; - for(uint8_t i = 0; i < pos->childCount; i++) { - entityposition_t *child = componentGetData( - pos->childEntityIds[i], pos->childComponentIds[i], COMPONENT_TYPE_POSITION - ); - entityPositionMarkDirty(child); - } -} - -void entityPositionInit( - const entityid_t entityId, - const componentid_t componentId -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - - pos->flags = 0; - pos->parentEntityId = ENTITY_ID_INVALID; - pos->parentComponentId = COMPONENT_ID_INVALID; - pos->childCount = 0; - glm_vec3_zero(pos->position); - glm_vec3_zero(pos->rotation); - glm_vec3_one(pos->scale); - glm_mat4_identity(pos->localTransform); - glm_mat4_identity(pos->worldTransform); -} - -void entityPositionLookAt( - const entityid_t entityId, - const componentid_t componentId, - vec3 eye, - vec3 target, - vec3 up -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - glm_lookat(eye, target, up, pos->localTransform); - // localTransform is now authoritative; PRS cache is stale. - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_PRS_DIRTY) - & ~(ENTITY_POSITION_FLAG_ROTATION_DIRTY | - ENTITY_POSITION_FLAG_POSITION_DIRTY); - entityPositionMarkDirty(pos); -} - -void entityPositionGetTransform( - const entityid_t entityId, - const componentid_t componentId, - mat4 dest -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsureWorld(pos); - glm_mat4_copy( - pos->parentEntityId == ENTITY_ID_INVALID - ? pos->localTransform : pos->worldTransform, - dest - ); -} - -void entityPositionGetLocalTransform( - const entityid_t entityId, - const componentid_t componentId, - mat4 dest -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsureLocal(pos); - glm_mat4_copy(pos->localTransform, dest); -} - -void entityPositionGetLocalPosition( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsurePRS(pos); - glm_vec3_copy(pos->position, dest); -} - -void entityPositionGetWorldPosition( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - if(pos->parentEntityId == ENTITY_ID_INVALID) { - entityPositionEnsurePRS(pos); - glm_vec3_copy(pos->position, dest); - return; - } - entityPositionEnsureWorld(pos); - dest[0] = pos->worldTransform[3][0]; - dest[1] = pos->worldTransform[3][1]; - dest[2] = pos->worldTransform[3][2]; -} - -void entityPositionSetWorldPosition( - const entityid_t entityId, - const componentid_t componentId, - vec3 position -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - if(pos->parentEntityId == ENTITY_ID_INVALID) { - glm_vec3_copy(position, pos->position); - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_POSITION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); - return; - } - entityposition_t *parent = componentGetData( - pos->parentEntityId, pos->parentComponentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsureWorld(parent); - mat4 invParent; - glm_mat4_inv(parent->worldTransform, invParent); - vec3 localPos; - glm_mat4_mulv3(invParent, position, 1.0f, localPos); - glm_vec3_copy(localPos, pos->position); - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_POSITION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); -} - -void entityPositionSetLocalPosition( - const entityid_t entityId, - const componentid_t componentId, - vec3 position -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - glm_vec3_copy(position, pos->position); - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_POSITION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); -} - -void entityPositionGetLocalRotation( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsurePRS(pos); - glm_vec3_copy(pos->rotation, dest); -} - -void entityPositionGetWorldRotation( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - if(pos->parentEntityId == ENTITY_ID_INVALID) { - entityPositionEnsurePRS(pos); - glm_vec3_copy(pos->rotation, dest); - return; - } - entityPositionEnsureWorld(pos); - const float_t (*wt)[4] = pos->worldTransform; - const float_t sx = sqrtf( - wt[0][0]*wt[0][0] + wt[0][1]*wt[0][1] + wt[0][2]*wt[0][2] - ); - const float_t sy = sqrtf( - wt[1][0]*wt[1][0] + wt[1][1]*wt[1][1] + wt[1][2]*wt[1][2] - ); - const float_t sz = sqrtf( - wt[2][0]*wt[2][0] + wt[2][1]*wt[2][1] + wt[2][2]*wt[2][2] - ); - const float_t r00 = sx > 0.0f ? wt[0][0]/sx : 0.0f; - const float_t r10 = sy > 0.0f ? wt[1][0]/sy : 0.0f; - const float_t r20 = sz > 0.0f ? wt[2][0]/sz : 0.0f; - const float_t r01 = sx > 0.0f ? wt[0][1]/sx : 0.0f; - const float_t r11 = sy > 0.0f ? wt[1][1]/sy : 0.0f; - const float_t r21 = sz > 0.0f ? wt[2][1]/sz : 0.0f; - const float_t r22 = sz > 0.0f ? wt[2][2]/sz : 0.0f; - const float_t sinBeta = glm_clamp(r20, -1.0f, 1.0f); - dest[1] = asinf(sinBeta); - const float_t cosBeta = cosf(dest[1]); - if(fabsf(cosBeta) > 1e-6f) { - dest[0] = atan2f(-r21, r22); - dest[2] = atan2f(-r10, r00); - } else { - dest[2] = 0.0f; - dest[0] = (sinBeta > 0.0f) ? atan2f(r01, r11) : -atan2f(r01, r11); - } -} - -void entityPositionSetLocalRotation( - const entityid_t entityId, - const componentid_t componentId, - vec3 rotation -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - glm_vec3_copy(rotation, pos->rotation); - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); -} - -void entityPositionSetWorldRotation( - const entityid_t entityId, - const componentid_t componentId, - vec3 rotation -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - if(pos->parentEntityId == ENTITY_ID_INVALID) { - glm_vec3_copy(rotation, pos->rotation); - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); - return; - } - entityposition_t *parent = componentGetData( - pos->parentEntityId, pos->parentComponentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsureWorld(parent); - - // Build target world rotation matrix (unit scale) from XYZ euler. - const float_t c0 = cosf(rotation[0]), s0 = sinf(rotation[0]); - const float_t c1 = cosf(rotation[1]), s1 = sinf(rotation[1]); - const float_t c2 = cosf(rotation[2]), s2 = sinf(rotation[2]); - const float_t s0s1 = s0*s1, c0s1 = c0*s1; - // Named wr[col_stored][row_stored] matching cglm column-major layout. - const float_t wr00 = c1*c2; - const float_t wr01 = c0*s2 + s0s1*c2; - const float_t wr02 = s0*s2 - c0s1*c2; - const float_t wr10 = -c1*s2; - const float_t wr11 = c0*c2 - s0s1*s2; - const float_t wr12 = s0*c2 + c0s1*s2; - const float_t wr20 = s1; - const float_t wr21 = -s0*c1; - const float_t wr22 = c0*c1; - - // Normalize parent world columns to extract pure rotation. - const float_t (*pt)[4] = parent->worldTransform; - const float_t psx = sqrtf( - pt[0][0]*pt[0][0] + pt[0][1]*pt[0][1] + pt[0][2]*pt[0][2] - ); - const float_t psy = sqrtf( - pt[1][0]*pt[1][0] + pt[1][1]*pt[1][1] + pt[1][2]*pt[1][2] - ); - const float_t psz = sqrtf( - pt[2][0]*pt[2][0] + pt[2][1]*pt[2][1] + pt[2][2]*pt[2][2] - ); - const float_t pr00 = psx > 0.f ? pt[0][0]/psx : 0.f; - const float_t pr01 = psx > 0.f ? pt[0][1]/psx : 0.f; - const float_t pr02 = psx > 0.f ? pt[0][2]/psx : 0.f; - const float_t pr10 = psy > 0.f ? pt[1][0]/psy : 0.f; - const float_t pr11 = psy > 0.f ? pt[1][1]/psy : 0.f; - const float_t pr12 = psy > 0.f ? pt[1][2]/psy : 0.f; - const float_t pr20 = psz > 0.f ? pt[2][0]/psz : 0.f; - const float_t pr21 = psz > 0.f ? pt[2][1]/psz : 0.f; - const float_t pr22 = psz > 0.f ? pt[2][2]/psz : 0.f; - - // local_R = parent_R^T * world_R (R^-1 == R^T for orthogonal matrices). - // Compute only the 7 entries of the local rotation matrix needed for XYZ - // euler extraction (stored column-major: [col][row] = math [row][col]). - // sinBeta = stored[2][0] = math[0][2] - // r21/r22 = stored[2][1..2] = math[1..2][2] - // r10/r00 = stored[1][0], stored[0][0] = math[0][1], math[0][0] - // gimbal = stored[0][1], stored[1][1] = math[1][0], math[1][1] - const float_t lr00 = pr00*wr00 + pr01*wr10 + pr02*wr20; // math[0][0] - const float_t lr10 = pr00*wr01 + pr01*wr11 + pr02*wr21; // math[0][1] - const float_t lr20 = pr00*wr02 + pr01*wr12 + pr02*wr22; // [0][2] -> sinBeta - const float_t lr01 = pr10*wr00 + pr11*wr10 + pr12*wr20; // math[1][0] - const float_t lr11 = pr10*wr01 + pr11*wr11 + pr12*wr21; // math[1][1] - const float_t lr21 = pr10*wr02 + pr11*wr12 + pr12*wr22; // [1][2] -> r21 - const float_t lr22 = pr20*wr02 + pr21*wr12 + pr22*wr22; // [2][2] -> r22 - - const float_t sinBeta = glm_clamp(lr20, -1.0f, 1.0f); - pos->rotation[1] = asinf(sinBeta); - const float_t cosBeta = cosf(pos->rotation[1]); - if(fabsf(cosBeta) > 1e-6f) { - pos->rotation[0] = atan2f(-lr21, lr22); - pos->rotation[2] = atan2f(-lr10, lr00); - } else { - pos->rotation[2] = 0.0f; - pos->rotation[0] = (sinBeta > 0.0f) - ? atan2f(lr01, lr11) : -atan2f(lr01, lr11); - } - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); -} - -void entityPositionGetLocalScale( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsurePRS(pos); - glm_vec3_copy(pos->scale, dest); -} - -void entityPositionGetWorldScale( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - if(pos->parentEntityId == ENTITY_ID_INVALID) { - entityPositionEnsurePRS(pos); - glm_vec3_copy(pos->scale, dest); - return; - } - entityPositionEnsureWorld(pos); - const float_t (*wt)[4] = pos->worldTransform; - dest[0] = sqrtf(wt[0][0]*wt[0][0] + wt[0][1]*wt[0][1] + wt[0][2]*wt[0][2]); - dest[1] = sqrtf(wt[1][0]*wt[1][0] + wt[1][1]*wt[1][1] + wt[1][2]*wt[1][2]); - dest[2] = sqrtf(wt[2][0]*wt[2][0] + wt[2][1]*wt[2][1] + wt[2][2]*wt[2][2]); -} - -void entityPositionSetLocalScale( - const entityid_t entityId, - const componentid_t componentId, - vec3 scale -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - glm_vec3_copy(scale, pos->scale); - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); -} - -void entityPositionSetWorldScale( - const entityid_t entityId, - const componentid_t componentId, - vec3 scale -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - if(pos->parentEntityId == ENTITY_ID_INVALID) { - glm_vec3_copy(scale, pos->scale); - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); - return; - } - entityposition_t *parent = componentGetData( - pos->parentEntityId, pos->parentComponentId, COMPONENT_TYPE_POSITION - ); - entityPositionEnsureWorld(parent); - const float_t (*pt)[4] = parent->worldTransform; - const float_t psx = sqrtf( - pt[0][0]*pt[0][0] + pt[0][1]*pt[0][1] + pt[0][2]*pt[0][2] - ); - const float_t psy = sqrtf( - pt[1][0]*pt[1][0] + pt[1][1]*pt[1][1] + pt[1][2]*pt[1][2] - ); - const float_t psz = sqrtf( - pt[2][0]*pt[2][0] + pt[2][1]*pt[2][1] + pt[2][2]*pt[2][2] - ); - pos->scale[0] = psx > 0.0f ? scale[0] / psx : scale[0]; - pos->scale[1] = psy > 0.0f ? scale[1] / psy : scale[1]; - pos->scale[2] = psz > 0.0f ? scale[2] / psz : scale[2]; - pos->flags = (pos->flags | ENTITY_POSITION_FLAG_ROTATION_DIRTY) - & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); -} - -void entityPositionSetParent( - const entityid_t entityId, - const componentid_t componentId, - const entityid_t parentEntityId, - const componentid_t parentComponentId -) { - entityposition_t *pos = componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); - - // Remove from old parent's child list. - if(pos->parentEntityId != ENTITY_ID_INVALID) { - entityposition_t *oldParent = componentGetData( - pos->parentEntityId, pos->parentComponentId, COMPONENT_TYPE_POSITION - ); - for(uint8_t i = 0; i < oldParent->childCount; i++) { - if( - oldParent->childEntityIds[i] == entityId && - oldParent->childComponentIds[i] == componentId - ) { - oldParent->childCount--; - for(uint8_t j = i; j < oldParent->childCount; j++) { - oldParent->childEntityIds[j] = oldParent->childEntityIds[j + 1]; - oldParent->childComponentIds[j] = oldParent->childComponentIds[j + 1]; - } - break; - } - } - } - - pos->parentEntityId = parentEntityId; - pos->parentComponentId = parentComponentId; - - // Register with new parent. - if(parentEntityId != ENTITY_ID_INVALID) { - entityposition_t *parent = componentGetData( - parentEntityId, parentComponentId, COMPONENT_TYPE_POSITION - ); - if(parent->childCount < ENTITY_POSITION_CHILDREN_MAX) { - parent->childEntityIds[parent->childCount] = entityId; - parent->childComponentIds[parent->childCount] = componentId; - parent->childCount++; - } - } - - entityPositionMarkDirty(pos); -} - -entityposition_t *entityPositionGet( - const entityid_t entityId, - const componentid_t componentId -) { - return componentGetData( - entityId, componentId, COMPONENT_TYPE_POSITION - ); -} - -void entityPositionRebuild(entityposition_t *pos) { - pos->flags = ( - pos->flags | - ENTITY_POSITION_FLAG_ROTATION_DIRTY | - ENTITY_POSITION_FLAG_POSITION_DIRTY - ) & ~ENTITY_POSITION_FLAG_PRS_DIRTY; - entityPositionMarkDirty(pos); -} - -void entityPositionDisposeDeep( - const entityid_t entityId, - const componentid_t componentId -) { - entityposition_t *pos = entityPositionGet(entityId, componentId); - - // Detach from parent so the parent's child list stays consistent. - if(pos->parentEntityId != ENTITY_ID_INVALID) { - entityPositionSetParent( - entityId, componentId, ENTITY_ID_INVALID, COMPONENT_ID_INVALID - ); - } - - // Copy the child list before disposing self (entityDispose invalidates pos). - uint8_t childCount = pos->childCount; - entityid_t childEntityIds[ENTITY_POSITION_CHILDREN_MAX]; - componentid_t childComponentIds[ENTITY_POSITION_CHILDREN_MAX]; - for(uint8_t i = 0; i < childCount; i++) { - childEntityIds[i] = pos->childEntityIds[i]; - childComponentIds[i] = pos->childComponentIds[i]; - // Sever child's parent link so it won't try to modify our disposed data. - entityposition_t *child = entityPositionGet( - childEntityIds[i], childComponentIds[i] - ); - child->parentEntityId = ENTITY_ID_INVALID; - child->parentComponentId = COMPONENT_ID_INVALID; - } - - entityDispose(entityId); - - for(uint8_t i = 0; i < childCount; i++) { - entityPositionDisposeDeep(childEntityIds[i], childComponentIds[i]); - } -} - -void entityPositionDecompose(entityposition_t *pos) { - // Translation: column 3 - pos->position[0] = pos->localTransform[3][0]; - pos->position[1] = pos->localTransform[3][1]; - pos->position[2] = pos->localTransform[3][2]; - - // Scale: length of each basis column (xyz only) - pos->scale[0] = sqrtf( - pos->localTransform[0][0] * pos->localTransform[0][0] + - pos->localTransform[0][1] * pos->localTransform[0][1] + - pos->localTransform[0][2] * pos->localTransform[0][2] - ); - pos->scale[1] = sqrtf( - pos->localTransform[1][0] * pos->localTransform[1][0] + - pos->localTransform[1][1] * pos->localTransform[1][1] + - pos->localTransform[1][2] * pos->localTransform[1][2] - ); - pos->scale[2] = sqrtf( - pos->localTransform[2][0] * pos->localTransform[2][0] + - pos->localTransform[2][1] * pos->localTransform[2][1] + - pos->localTransform[2][2] * pos->localTransform[2][2] - ); - - // Normalize columns to isolate the rotation matrix (no mat4 needed). - const float_t invS0 = pos->scale[0] > 0.0f ? 1.0f / pos->scale[0] : 0.0f; - const float_t invS1 = pos->scale[1] > 0.0f ? 1.0f / pos->scale[1] : 0.0f; - const float_t invS2 = pos->scale[2] > 0.0f ? 1.0f / pos->scale[2] : 0.0f; - - const float_t r00 = pos->localTransform[0][0] * invS0; - const float_t r01 = pos->localTransform[0][1] * invS0; - const float_t r02 = pos->localTransform[0][2] * invS0; - const float_t r10 = pos->localTransform[1][0] * invS1; - const float_t r11 = pos->localTransform[1][1] * invS1; - const float_t r20 = pos->localTransform[2][0] * invS2; - const float_t r21 = pos->localTransform[2][1] * invS2; - const float_t r22 = pos->localTransform[2][2] * invS2; - - // Extract XYZ euler angles (R = Rx * Ry * Rz, column-major) - const float_t sinBeta = glm_clamp(r20, -1.0f, 1.0f); - pos->rotation[1] = asinf(sinBeta); - const float_t cosBeta = cosf(pos->rotation[1]); - - if(fabsf(cosBeta) > 1e-6f) { - pos->rotation[0] = atan2f(-r21, r22); - pos->rotation[2] = atan2f(-r10, r00); - } else { - // Gimbal lock: pin Z to 0, recover X. - pos->rotation[2] = 0.0f; - pos->rotation[0] = (sinBeta > 0.0f) - ? atan2f(r01, r11) - : -atan2f(r01, r11); - } -} diff --git a/src/dusk/entity/component/display/entityposition.h b/src/dusk/entity/component/display/entityposition.h deleted file mode 100644 index 391a58c3..00000000 --- a/src/dusk/entity/component/display/entityposition.h +++ /dev/null @@ -1,375 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entity/entitybase.h" - -/** Maximum number of child position components this node can track. */ -#define ENTITY_POSITION_CHILDREN_MAX 8 - -/** - * PRS cache is stale. localTransform was written directly (e.g. lookAt) and - * position/rotation/scale need to be decomposed before they can be read. - */ -#define ENTITY_POSITION_FLAG_PRS_DIRTY (1 << 0) - -/** - * Columns 0-2 of localTransform are stale. Rotation or scale changed; the - * basis vectors need to be rebuilt analytically before the matrix can be used. - * Does not imply column 3 (translation) is stale. - */ -#define ENTITY_POSITION_FLAG_ROTATION_DIRTY (1 << 1) - -/** - * Column 3 of localTransform is stale. Position changed; only the translation - * column needs to be written. Does not imply columns 0-2 are stale. - */ -#define ENTITY_POSITION_FLAG_POSITION_DIRTY (1 << 2) - -/** - * worldTransform is stale. Either the local matrix changed or an ancestor - * moved; worldTransform must be recomputed before world data can be read. - */ -#define ENTITY_POSITION_FLAG_WORLD_DIRTY (1 << 3) - -typedef struct { - /* - * Hot fields - flag checks, parent/child traversal (markDirty, ensureWorld) - * only touch these. Kept at the front so they share the first cache line. - */ - - /** ENTITY_POSITION_FLAG_* bitmask; describes which caches are stale. */ - uint8_t flags; - /** Entity ID of the parent node, or ENTITY_ID_INVALID if none. */ - entityid_t parentEntityId; - /** Component ID of the parent position, or COMPONENT_ID_INVALID if none. */ - componentid_t parentComponentId; - /** Number of currently registered children. */ - uint8_t childCount; - /** Entity IDs of child nodes. */ - entityid_t childEntityIds[ENTITY_POSITION_CHILDREN_MAX]; - /** Component IDs of child position components. */ - componentid_t childComponentIds[ENTITY_POSITION_CHILDREN_MAX]; - - /* - * Warm fields - read/written by PRS getters/setters. - * Accessed more often than the matrices but less often than flags. - */ - - /** Cached local position (XYZ). Stale when PRS_DIRTY is set. */ - vec3 position; - /** Cached local rotation (XYZ euler, radians). Stale when PRS_DIRTY. */ - vec3 rotation; - /** Cached local scale (XYZ). Stale when PRS_DIRTY is set. */ - vec3 scale; - - /* - * Cold fields - only touched when actually rebuilding transforms. - */ - - /** Local transform matrix, rebuilt lazily from position/rotation/scale. */ - mat4 localTransform; - /** World transform matrix, recomputed lazily from the parent chain. */ - mat4 worldTransform; -} entityposition_t; - -/** - * Initializes the entity position component, setting identity transforms and - * zeroing all parent/child state. - * - * @param entityId The entity ID. - * @param componentId The component ID. - */ -void entityPositionInit( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Transforms the entity's local transform to look at a target point. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param eye The eye/camera position. - * @param target The target point to look at. - * @param up The up vector. - */ -void entityPositionLookAt( - const entityid_t entityId, - const componentid_t componentId, - vec3 eye, - vec3 target, - vec3 up -); - -/** - * Gets the world-space transform matrix, recomputing it lazily if dirty. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest Destination matrix. - */ -void entityPositionGetTransform( - const entityid_t entityId, - const componentid_t componentId, - mat4 dest -); - -/** - * Gets the local transform matrix (does not include parent transforms). - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest Destination matrix. - */ -void entityPositionGetLocalTransform( - const entityid_t entityId, - const componentid_t componentId, - mat4 dest -); - -/** - * Gets the cached local position (XYZ). Decomposes localTransform into PRS - * first if ENTITY_POSITION_FLAG_PRS_DIRTY is set; never triggers a matrix - * rebuild or world-transform update. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest Destination vector. - */ -void entityPositionGetLocalPosition( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -); - -/** - * Gets the world-space position. For parentless entities this is the same as - * the local position. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest Destination vector. - */ -void entityPositionGetWorldPosition( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -); - -/** - * Sets the world-space position. For parentless entities this is equivalent to - * entityPositionSetLocalPosition. For parented entities the position is - * converted to local space via the inverted parent world transform. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param position The desired world-space position. - */ -void entityPositionSetWorldPosition( - const entityid_t entityId, - const componentid_t componentId, - vec3 position -); - -/** - * Sets the local position, marks localTransform and worldTransform (self + - * descendants) dirty. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param position The new local position. - */ -void entityPositionSetLocalPosition( - const entityid_t entityId, - const componentid_t componentId, - vec3 position -); - -/** - * Gets the cached local euler rotation (XYZ, radians). Decomposes - * localTransform first if ENTITY_POSITION_FLAG_PRS_DIRTY is set. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest Destination vector. - */ -void entityPositionGetLocalRotation( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -); - -/** - * Gets the world-space euler rotation (XYZ, radians) by decomposing the world - * transform. For parentless entities this is the same as local rotation. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest Destination vector. - */ -void entityPositionGetWorldRotation( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -); - -/** - * Sets the local euler rotation (XYZ, radians) and marks transforms dirty. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param rotation The new local rotation. - */ -void entityPositionSetLocalRotation( - const entityid_t entityId, - const componentid_t componentId, - vec3 rotation -); - -/** - * Sets the world-space euler rotation (XYZ, radians). For parentless entities - * this is equivalent to entityPositionSetLocalRotation. For parented entities - * the rotation is converted to local space by removing the parent world - * rotation. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param rotation The desired world-space euler rotation. - */ -void entityPositionSetWorldRotation( - const entityid_t entityId, - const componentid_t componentId, - vec3 rotation -); - -/** - * Gets the cached local scale. Decomposes localTransform first if - * ENTITY_POSITION_FLAG_PRS_DIRTY is set. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest Destination vector. - */ -void entityPositionGetLocalScale( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -); - -/** - * Gets the world-space scale by extracting column lengths from the world - * transform. For parentless entities this is the same as local scale. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest Destination vector. - */ -void entityPositionGetWorldScale( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -); - -/** - * Sets the local scale and marks transforms dirty. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param scale The new local scale. - */ -void entityPositionSetLocalScale( - const entityid_t entityId, - const componentid_t componentId, - vec3 scale -); - -/** - * Sets the world-space scale. For parentless entities this is equivalent to - * entityPositionSetLocalScale. For parented entities the scale is converted to - * local space by dividing by the parent world scale (assumes no shear). - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param scale The desired world-space scale. - */ -void entityPositionSetWorldScale( - const entityid_t entityId, - const componentid_t componentId, - vec3 scale -); - -/** - * Sets the parent of this entity's position component. - * Pass ENTITY_ID_INVALID / COMPONENT_ID_INVALID to detach from any parent. - * - * @param entityId The child entity ID. - * @param componentId The child component ID. - * @param parentEntityId The parent entity ID. - * @param parentComponentId The parent component ID. - */ -void entityPositionSetParent( - const entityid_t entityId, - const componentid_t componentId, - const entityid_t parentEntityId, - const componentid_t parentComponentId -); - -/** - * Returns a direct pointer to the entity position component data. - * After modifying localTransform directly, call entityPositionMarkDirty() to - * set ENTITY_POSITION_FLAG_WORLD_DIRTY on self and descendants. After - * modifying PRS directly, call entityPositionRebuild() instead. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @return Pointer to the component data. - */ -entityposition_t *entityPositionGet( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Signals that the PRS cache was modified externally. Sets both - * ENTITY_POSITION_FLAG_ROTATION_DIRTY and ENTITY_POSITION_FLAG_POSITION_DIRTY - * so all of localTransform is rebuilt lazily on the next read, clears - * ENTITY_POSITION_FLAG_PRS_DIRTY, propagates ENTITY_POSITION_FLAG_WORLD_DIRTY - * to self and all descendants. - * - * @param pos The position component whose PRS was modified. - */ -void entityPositionRebuild(entityposition_t *pos); - -/** - * Sets ENTITY_POSITION_FLAG_WORLD_DIRTY on this node and all descendants, - * indicating that worldTransform must be recomputed before it is read. - * Call this after modifying localTransform directly. - * - * @param pos The position component to mark dirty. - */ -void entityPositionMarkDirty(entityposition_t *pos); - -/** - * Disposes this entity and all of its position-component descendants - * recursively. Detaches from any parent before destroying. - * - * @param entityId The root entity ID. - * @param componentId The root position component ID. - */ -void entityPositionDisposeDeep( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Decomposes the local transform matrix back into the position, rotation - * (XYZ euler, radians), and scale cache fields. - * - * @param pos The position component to decompose. - */ -void entityPositionDecompose(entityposition_t *pos); diff --git a/src/dusk/entity/component/display/entityrenderable.c b/src/dusk/entity/component/display/entityrenderable.c deleted file mode 100644 index a30fa1be..00000000 --- a/src/dusk/entity/component/display/entityrenderable.c +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entityrenderable.h" -#include "entity/entitymanager.h" -#include "display/shader/shadermaterial.h" -#include "display/shader/shaderunlit.h" -#include "display/display.h" -#include "display/mesh/cube.h" -#include "util/memory.h" -#include "assert/assert.h" - -void entityRenderableInit( - const entityid_t entityId, - const componentid_t componentId -) { - entityrenderable_t *r = componentGetData( - entityId, componentId, COMPONENT_TYPE_RENDERABLE - ); - memoryZero(r, sizeof(entityrenderable_t)); - r->type = ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL; - r->data.material.shaderType = SHADER_LIST_SHADER_UNLIT; - r->data.material.material.unlit.color = COLOR_WHITE; - r->data.material.meshes[0] = &CUBE_MESH_SIMPLE; - r->data.material.meshOffsets[0] = 0; - r->data.material.meshCounts[0] = -1; - r->data.material.meshCount = 1; - r->data.material.state.flags = DISPLAY_STATE_FLAG_DEPTH_TEST; -} - -void entityRenderableDispose( - const entityid_t entityId, - const componentid_t componentId -) { -} - -void entityRenderableSetType( - const entityid_t entityId, - const componentid_t componentId, - const entityrenderabletype_t type -) { - entityrenderable_t *r = componentGetData( - entityId, componentId, COMPONENT_TYPE_RENDERABLE - ); - r->type = type; -} - -void entityRenderableSetPriority( - const entityid_t entityId, - const componentid_t componentId, - const int8_t priority -) { - entityrenderable_t *r = componentGetData( - entityId, componentId, COMPONENT_TYPE_RENDERABLE - ); - r->priority = priority; -} - -void entityRenderableSetDraw( - const entityid_t entityId, - const componentid_t componentId, - errorret_t (*draw)( - const entityid_t entityId, - const componentid_t componentId, - void *user - ), - void *user -) { - assertNotNull(draw, "Draw callback cannot be null"); - entityrenderable_t *r = componentGetData( - entityId, componentId, COMPONENT_TYPE_RENDERABLE - ); - r->type = ENTITY_RENDERABLE_TYPE_CUSTOM; - r->data.custom.draw = draw; - r->data.custom.drawUser = user; -} - -static errorret_t entityRenderableDrawSpritebatch( - const entityrenderablespritebatch_t *sb -) { - if(sb->spriteCount == 0) errorOk(); - - errorChain(displaySetState((displaystate_t){ - .flags = DISPLAY_STATE_FLAG_BLEND - })); - - spriteBatchClear(); - shadermaterial_t mat; - memoryZero(&mat, sizeof(shadermaterial_t)); - mat.unlit.texture = sb->texture; - mat.unlit.color = COLOR_WHITE; - errorChain(spriteBatchBuffer( - sb->sprites, sb->spriteCount, - SHADER_LIST_DEFS[SHADER_LIST_SHADER_UNLIT].shader, mat - )); - return spriteBatchFlush(); -} - -static errorret_t entityRenderableDrawMaterial( - const entityrenderablematerial_t *m -) { - errorChain(displaySetState(m->state)); - shader_t *shader = SHADER_LIST_DEFS[m->shaderType].shader; - assertNotNull(shader, "Shader cannot be null for material type"); - errorChain(shaderBind(shader)); - errorChain(shaderSetMaterial(shader, &m->material)); - for(uint8_t i = 0; i < m->meshCount; i++) { - errorChain(meshDraw(m->meshes[i], m->meshOffsets[i], m->meshCounts[i])); - } - errorOk(); -} - -static errorret_t entityRenderableDrawCustom( - const entityid_t entityId, - const componentid_t componentId, - const entityrenderablecustom_t *custom -) { - return custom->draw(entityId, componentId, custom->drawUser); -} - -errorret_t entityRenderableDraw( - const entityid_t entityId, - const componentid_t componentId -) { - entityrenderable_t *r = componentGetData( - entityId, componentId, COMPONENT_TYPE_RENDERABLE - ); - switch(r->type) { - case ENTITY_RENDERABLE_TYPE_SPRITEBATCH: - return entityRenderableDrawSpritebatch(&r->data.spritebatch); - case ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL: - return entityRenderableDrawMaterial(&r->data.material); - case ENTITY_RENDERABLE_TYPE_CUSTOM: - return entityRenderableDrawCustom(entityId, componentId, &r->data.custom); - default: - assertUnreachable("Invalid renderable type"); - } -} diff --git a/src/dusk/entity/component/display/entityrenderable.h b/src/dusk/entity/component/display/entityrenderable.h deleted file mode 100644 index 1d87243a..00000000 --- a/src/dusk/entity/component/display/entityrenderable.h +++ /dev/null @@ -1,147 +0,0 @@ -/** - * 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 "display/mesh/mesh.h" -#include "display/shader/shadermaterial.h" -#include "display/spritebatch/spritebatch.h" -#include "display/displaystate.h" - -#define ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX 64 -#define ENTITY_RENDERABLE_MESHES_MAX 8 - -typedef enum { - ENTITY_RENDERABLE_TYPE_CUSTOM = 0, - ENTITY_RENDERABLE_TYPE_SPRITEBATCH, - ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL, -} entityrenderabletype_t; - -typedef struct { - spritebatchsprite_t sprites[ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX]; - uint32_t spriteCount; - texture_t *texture; -} entityrenderablespritebatch_t; - -typedef struct { - mesh_t *meshes[ENTITY_RENDERABLE_MESHES_MAX]; - int32_t meshOffsets[ENTITY_RENDERABLE_MESHES_MAX]; - int32_t meshCounts[ENTITY_RENDERABLE_MESHES_MAX]; - uint8_t meshCount; - shaderlistshader_t shaderType; - shadermaterial_t material; - displaystate_t state; -} entityrenderablematerial_t; - -typedef struct { - errorret_t (*draw)( - const entityid_t entityId, - const componentid_t componentId, - void *user - ); - void *drawUser; -} entityrenderablecustom_t; - -typedef union entityrenderabledata_u { - entityrenderablespritebatch_t spritebatch; - entityrenderablematerial_t material; - entityrenderablecustom_t custom; -} entityrenderabledata_t; - -typedef struct { - entityrenderabletype_t type; - entityrenderabledata_t data; - - /** - * Render priority. 0 = auto (derived from type/flags). Higher values render - * later (on top of lower values). Range: [-128..127] with 0 is auto. - */ - int8_t priority; -} entityrenderable_t; - -/** - * Initializes the entity renderable component. Defaults to - * ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL with the unlit shader, a white cube, - * and depth-test enabled. - * - * @param entityId The entity to initialize the component for. - * @param componentId The renderable component of the entity. - */ -void entityRenderableInit( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Disposes the entity renderable component. - * - * @param entityId The entity to dispose the component for. - * @param componentId The renderable component of the entity. - */ -void entityRenderableDispose( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Sets the rendering type for the renderable component. Resets type-specific - * data to zero. - * - * @param entityId The entity to configure. - * @param componentId The renderable component. - * @param type The rendering type to use. - */ -void entityRenderableSetType( - const entityid_t entityId, - const componentid_t componentId, - const entityrenderabletype_t type -); - -/** - * Sets the render priority. 0 = auto (derived from type/flags). Higher values - * render later (on top). Use non-zero to force ordering. - * - * @param entityId The entity to configure. - * @param componentId The renderable component. - * @param priority The priority value, or 0 for auto. - */ -void entityRenderableSetPriority( - const entityid_t entityId, - const componentid_t componentId, - const int8_t priority -); - -/** - * Sets the draw callback, switching the type to ENTITY_RENDERABLE_TYPE_CUSTOM. - * - * @param entityId The entity to configure. - * @param componentId The renderable component of the entity. - * @param draw The draw callback to assign. - * @param user Userdata passed to the callback. - */ -void entityRenderableSetDraw( - const entityid_t entityId, - const componentid_t componentId, - errorret_t (*draw)( - const entityid_t entityId, - const componentid_t componentId, - void *user - ), - void *user -); - -/** - * Draws the entity using its renderable component data. - * - * @param entityId The entity to draw. - * @param componentId The renderable component of the entity. - * @return Any error state that happened. - */ -errorret_t entityRenderableDraw( - const entityid_t entityId, - const componentid_t componentId -); diff --git a/src/dusk/entity/component/physics/entityphysics.c b/src/dusk/entity/component/physics/entityphysics.c deleted file mode 100644 index 3705f8b7..00000000 --- a/src/dusk/entity/component/physics/entityphysics.c +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entityphysics.h" -#include "entity/entitymanager.h" -#include "entity/component/display/entityposition.h" -#include "physics/physicsmanager.h" -#include "assert/assert.h" -#include "util/memory.h" - -void entityPhysicsInit( - const entityid_t entityId, - const componentid_t componentId -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - - memoryZero(phys, sizeof(entityphysics_t)); - - // Default to cube - phys->type = PHYSICS_BODY_DYNAMIC; - phys->shape.type = PHYSICS_SHAPE_CUBE; - phys->shape.data.cube.halfExtents[0] = 0.5f; - phys->shape.data.cube.halfExtents[1] = 0.5f; - phys->shape.data.cube.halfExtents[2] = 0.5f; - phys->gravityScale = 1.0f; - phys->onGround = false; -} - -entityphysics_t *entityPhysicsGet( - const entityid_t entityId, - const componentid_t componentId -) { - return componentGetData(entityId, componentId, COMPONENT_TYPE_PHYSICS); -} - -void entityPhysicsSetShape( - const entityid_t entityId, - const componentid_t componentId, - const physicsshape_t shape -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - phys->shape = shape; - // TODO: Do I need to reset the state for ground/active? -} - -physicsshape_t entityPhysicsGetShape( - const entityid_t entityId, - const componentid_t componentId -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - return phys->shape; -} - -void entityPhysicsGetVelocity( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - - glm_vec3_copy(phys->velocity, dest); -} - -void entityPhysicsSetVelocity( - const entityid_t entityId, - const componentid_t componentId, - vec3 velocity -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - glm_vec3_copy(velocity, phys->velocity); -} - -void entityPhysicsApplyImpulse( - const entityid_t entityId, - const componentid_t componentId, - vec3 impulse -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - - if(phys->type == PHYSICS_BODY_STATIC) return; - glm_vec3_add(phys->velocity, impulse, phys->velocity); -} - -bool_t entityPhysicsIsOnGround( - const entityid_t entityId, - const componentid_t componentId -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - return phys->onGround; -} - -void entityPhysicsSetBodyType( - const entityid_t entityId, - const componentid_t componentId, - const physicsbodytype_t type -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - phys->type = type; -} - -physicsbodytype_t entityPhysicsGetBodyType( - const entityid_t entityId, - const componentid_t componentId -) { - entityphysics_t *phys = entityPhysicsGet(entityId, componentId); - assertNotNull(phys, "Failed to get physics component data"); - return phys->type; -} - -void entityPhysicsDispose( - const entityid_t entityId, - const componentid_t componentId -) { -} diff --git a/src/dusk/entity/component/physics/entityphysics.h b/src/dusk/entity/component/physics/entityphysics.h deleted file mode 100644 index 6cccfca3..00000000 --- a/src/dusk/entity/component/physics/entityphysics.h +++ /dev/null @@ -1,157 +0,0 @@ -/** - * 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 "physics/physicsshape.h" -#include "physics/physicsbodytype.h" - -typedef struct { - physicsbodytype_t type; - physicsshape_t shape; - vec3 velocity; - float_t gravityScale; - bool_t onGround; -} entityphysics_t; - -/** - * Initializes the physics component: allocates a body in PHYSICS_WORLD. - * Asserts if the world body limit is reached. - */ -void entityPhysicsInit( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Gets the underlying physics structure (temporarily) for the given entity. - * This is really just intended for doing operations faster than using the - * getters and setters, but it is preferred that you use those. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @return The physics component data for the given entity and component ID. - */ -entityphysics_t *entityPhysicsGet( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Sets the shape of the entity's physics body. This will not reset the body - * state, so if you change from a cube to a sphere, it will keep the same - * velocity and onGround state. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param shape The new shape to set on the physics body. - */ -void entityPhysicsSetShape( - const entityid_t entityId, - const componentid_t componentId, - const physicsshape_t shape -); - -/** - * Gets the shape of the entity's physics body. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @return The shape of the physics body. - */ -physicsshape_t entityPhysicsGetShape( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Gets the velocity of the entity's physics body. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param dest The destination vec3 to write the velocity to. - */ -void entityPhysicsGetVelocity( - const entityid_t entityId, - const componentid_t componentId, - vec3 dest -); - -/** - * Sets the velocity of the entity's physics body. This is not an impulse, so - * it will be affected by mass and drag. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param velocity The new velocity to set on the physics body. - */ -void entityPhysicsSetVelocity( - const entityid_t entityId, - const componentid_t componentId, - vec3 velocity -); - -/** - * Applies an impulse to the entity's physics body. This is an immediate - * velocity change that is not affected by mass or drag. No-op on STATIC bodies. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param impulse The impulse to apply to the physics body. - */ -void entityPhysicsApplyImpulse( - const entityid_t entityId, - const componentid_t componentId, - vec3 impulse -); - -/** - * Returns true if the entity's physics body rested on a surface during the last - * step or move. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @return True if the body is on the ground, false otherwise. - */ -bool_t entityPhysicsIsOnGround( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Sets the body type of the entity's physics body. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @param type The body type to set. - */ -void entityPhysicsSetBodyType( - const entityid_t entityId, - const componentid_t componentId, - const physicsbodytype_t type -); - -/** - * Gets the body type of the entity's physics body. - * - * @param entityId The entity ID. - * @param componentId The component ID. - * @return The body type of the physics body. - */ -physicsbodytype_t entityPhysicsGetBodyType( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Releases the body slot back to PHYSICS_WORLD. Called automatically when - * the component is disposed via the component system. - */ -void entityPhysicsDispose( - const entityid_t entityId, - const componentid_t componentId -); diff --git a/src/dusk/entity/component/trigger/entitytrigger.c b/src/dusk/entity/component/trigger/entitytrigger.c deleted file mode 100644 index bba57269..00000000 --- a/src/dusk/entity/component/trigger/entitytrigger.c +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entity/entitymanager.h" - -void entityTriggerInit( - const entityid_t entityId, - const componentid_t componentId -) { - entitytrigger_t *t = componentGetData( - entityId, componentId, COMPONENT_TYPE_TRIGGER - ); - glm_vec3_zero(t->min); - glm_vec3_zero(t->max); -} - -entitytrigger_t * entityTriggerGet( - const entityid_t entityId, - const componentid_t componentId -) { - return componentGetData(entityId, componentId, COMPONENT_TYPE_TRIGGER); -} - -bool_t entityTriggerContains( - const entityid_t entityId, - const componentid_t componentId, - const vec3 point -) { - entitytrigger_t *t = componentGetData( - entityId, componentId, COMPONENT_TYPE_TRIGGER - ); - return ( - point[0] >= t->min[0] && point[0] <= t->max[0] && - point[1] >= t->min[1] && point[1] <= t->max[1] && - point[2] >= t->min[2] && point[2] <= t->max[2] - ); -} - -void entityTriggerSetBounds( - const entityid_t entityId, - const componentid_t componentId, - const vec3 min, - const vec3 max -) { - entitytrigger_t *t = componentGetData( - entityId, componentId, COMPONENT_TYPE_TRIGGER - ); - glm_vec3_copy((float_t*)min, t->min); - glm_vec3_copy((float_t*)max, t->max); -} diff --git a/src/dusk/entity/component/trigger/entitytrigger.h b/src/dusk/entity/component/trigger/entitytrigger.h deleted file mode 100644 index fdbef2c8..00000000 --- a/src/dusk/entity/component/trigger/entitytrigger.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entity/entitybase.h" - -typedef struct { - vec3 min; - vec3 max; -} entitytrigger_t; - -/** - * Initializes the trigger component with zeroed bounds. - */ -void entityTriggerInit( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Returns a pointer to the trigger component data. - */ -entitytrigger_t * entityTriggerGet( - const entityid_t entityId, - const componentid_t componentId -); - -/** - * Returns true if the given world-space point lies within [min, max]. - */ -bool_t entityTriggerContains( - const entityid_t entityId, - const componentid_t componentId, - const vec3 point -); - -/** - * Sets both bounds at once. - */ -void entityTriggerSetBounds( - const entityid_t entityId, - const componentid_t componentId, - const vec3 min, - const vec3 max -); diff --git a/src/dusk/entity/componentlist.h b/src/dusk/entity/componentlist.h deleted file mode 100644 index b5f1bc82..00000000 --- a/src/dusk/entity/componentlist.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entity/component/display/entityposition.h" -#include "entity/component/display/entitycamera.h" -#include "entity/component/display/entityrenderable.h" -#include "entity/component/physics/entityphysics.h" -#include "entity/component/trigger/entitytrigger.h" - -// Name (Uppercase) -// Structure -// Field name (lowercase) -// Init function (optional) -// Dispose function (optional) -// Render function (optional) - -X(POSITION, entityposition_t, position, entityPositionInit, NULL, NULL) -X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL, NULL) -X(RENDERABLE, entityrenderable_t, renderable, - entityRenderableInit, entityRenderableDispose, NULL) -X(PHYSICS, entityphysics_t, physics, - entityPhysicsInit, entityPhysicsDispose, NULL) -X(TRIGGER, entitytrigger_t, trigger, entityTriggerInit, NULL, NULL) diff --git a/src/dusk/entity/entity.c b/src/dusk/entity/entity.c deleted file mode 100644 index bd71835a..00000000 --- a/src/dusk/entity/entity.c +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entitymanager.h" -#include "component/display/entityposition.h" -#include "util/memory.h" -#include "assert/assert.h" - -void entityInit(const entityid_t entityId) { - entity_t *ent = &ENTITY_MANAGER.entities[entityId]; - - memoryZero(ent, sizeof(entity_t)); - - // Mark all component types not using this entity. - for( - componenttype_t compType = 0; - compType < COMPONENT_TYPE_COUNT; - compType++ - ) { - ENTITY_MANAGER.entitiesWithComponent[ - compType * ENTITY_COUNT_MAX + entityId - ] = COMPONENT_ID_INVALID; - } - - ent->state |= ENTITY_STATE_ACTIVE; -} - -componentid_t entityAddComponent( - const entityid_t entityId, - const componenttype_t type -) { - componentindex_t compInd; - entity_t *ent = &ENTITY_MANAGER.entities[entityId]; - - for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) { - compInd = componentGetIndex(entityId, i); - if(ENTITY_MANAGER.components[compInd].type != COMPONENT_TYPE_NULL) { - assertTrue( - ENTITY_MANAGER.components[compInd].type != type, - "Entity already has component of this type" - ); - continue; - } - componentInit(entityId, i, type); - ENTITY_MANAGER.entitiesWithComponent[ - type * ENTITY_COUNT_MAX + entityId - ] = i; - return i; - } - - assertUnreachable("Entity has no more component slots available"); - return COMPONENT_ID_INVALID; -} - -componentid_t entityGetComponent( - const entityid_t entityId, - const componenttype_t type -) { - componentid_t compId = ENTITY_MANAGER.entitiesWithComponent[ - type * ENTITY_COUNT_MAX + entityId - ]; - if(compId == COMPONENT_ID_INVALID) return compId; - assertTrue( - ENTITY_MANAGER.components[componentGetIndex(entityId, compId)].type == type, - "Component type mismatch" - ); - return compId; -} - -void entityDisposeDeep(const entityid_t entityId) { - componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION); - if(posComp != COMPONENT_ID_INVALID) { - entityPositionDisposeDeep(entityId, posComp); - } else { - entityDispose(entityId); - } -} - -void entityUpdate(const entityid_t entityId) { - entity_t *ent = &ENTITY_MANAGER.entities[entityId]; - for(uint8_t i = 0; i < ent->updateCount; i++) { - ent->onUpdate[i](entityId, ent->updateComponentId[i], ent->updateUser[i]); - } -} - -void entityDispose(const entityid_t entityId) { - componentindex_t compInd; - entity_t *ent = &ENTITY_MANAGER.entities[entityId]; - - for(uint8_t i = 0; i < ent->disposeCount; i++) { - ent->onDispose[i]( - entityId, ent->disposeComponentId[i], ent->disposeUser[i] - ); - } - - for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) { - compInd = componentGetIndex(entityId, i); - componenttype_t type = ENTITY_MANAGER.components[compInd].type; - if(type == COMPONENT_TYPE_NULL) continue; - ENTITY_MANAGER.entitiesWithComponent[ - type * ENTITY_COUNT_MAX + entityId - ] = COMPONENT_ID_INVALID; - componentDispose(entityId, i); - } - - ent->state = 0; -} - -void entityUpdateAdd( - const entityid_t entityId, - const entitycallback_t callback, - const componentid_t componentId, - void *user -) { - entity_t *ent = &ENTITY_MANAGER.entities[entityId]; - assertTrue( - ent->updateCount < ENTITY_UPDATE_CALLBACK_COUNT_MAX, - "Entity update callback slots full" - ); - ent->onUpdate[ent->updateCount] = callback; - ent->updateComponentId[ent->updateCount] = componentId; - ent->updateUser[ent->updateCount] = user; - ent->updateCount++; -} - -void entityUpdateRemove( - const entityid_t entityId, - const entitycallback_t callback -) { - entity_t *ent = &ENTITY_MANAGER.entities[entityId]; - for(uint8_t i = 0; i < ent->updateCount; i++) { - if(ent->onUpdate[i] != callback) continue; - ent->updateCount--; - for(uint8_t j = i; j < ent->updateCount; j++) { - ent->onUpdate[j] = ent->onUpdate[j + 1]; - ent->updateComponentId[j] = ent->updateComponentId[j + 1]; - ent->updateUser[j] = ent->updateUser[j + 1]; - } - return; - } -} - -void entityDisposeAdd( - const entityid_t entityId, - const entitycallback_t callback, - const componentid_t componentId, - void *user -) { - entity_t *ent = &ENTITY_MANAGER.entities[entityId]; - assertTrue( - ent->disposeCount < ENTITY_DISPOSE_CALLBACK_COUNT_MAX, - "Entity dispose callback slots full" - ); - ent->onDispose[ent->disposeCount] = callback; - ent->disposeComponentId[ent->disposeCount] = componentId; - ent->disposeUser[ent->disposeCount] = user; - ent->disposeCount++; -} - -void entityDisposeRemove( - const entityid_t entityId, - const entitycallback_t callback -) { - entity_t *ent = &ENTITY_MANAGER.entities[entityId]; - for(uint8_t i = 0; i < ent->disposeCount; i++) { - if(ent->onDispose[i] != callback) continue; - ent->disposeCount--; - for(uint8_t j = i; j < ent->disposeCount; j++) { - ent->onDispose[j] = ent->onDispose[j + 1]; - ent->disposeComponentId[j] = ent->disposeComponentId[j + 1]; - ent->disposeUser[j] = ent->disposeUser[j + 1]; - } - return; - } -} \ No newline at end of file diff --git a/src/dusk/entity/entity.h b/src/dusk/entity/entity.h deleted file mode 100644 index 4845f6bb..00000000 --- a/src/dusk/entity/entity.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "component.h" - -#define ENTITY_STATE_ACTIVE (1 << 0) - -#define ENTITY_UPDATE_CALLBACK_COUNT_MAX 5 -#define ENTITY_DISPOSE_CALLBACK_COUNT_MAX 5 - -typedef void (*entitycallback_t)( - const entityid_t entityId, - const componentid_t componentId, - void *user -); - -typedef struct { - uint8_t state; - uint8_t updateCount; - uint8_t disposeCount; - entitycallback_t onUpdate[ENTITY_UPDATE_CALLBACK_COUNT_MAX]; - componentid_t updateComponentId[ENTITY_UPDATE_CALLBACK_COUNT_MAX]; - void *updateUser[ENTITY_UPDATE_CALLBACK_COUNT_MAX]; - entitycallback_t onDispose[ENTITY_DISPOSE_CALLBACK_COUNT_MAX]; - componentid_t disposeComponentId[ENTITY_DISPOSE_CALLBACK_COUNT_MAX]; - void *disposeUser[ENTITY_DISPOSE_CALLBACK_COUNT_MAX]; -} entity_t; - -/** - * Initializes an entity with the given ID. - * - * @param entityId The ID of the entity to initialize. - */ -void entityInit(const entityid_t entityId); - -/** - * Adds a component of the given type to the entity with the given ID. - * - * @param entityId The ID of the entity to add the component to. - * @param type The type of the component to add. - * @return The ID of the entity with component. - */ -componentid_t entityAddComponent( - const entityid_t entityId, - const componenttype_t type -); - -/** - * Gets the ID of the component of the given type on the entity with the given - * ID, or COMPONENT_ID_INVALID if the entity lacks the component. - * - * @param entityId The ID of the entity to get the component from. - * @param type The type of the component to get. - * @return The ID of the component. - */ -componentid_t entityGetComponent( - const entityid_t entityId, - const componenttype_t type -); - -/** - * Runs all registered update callbacks for the entity. - * - * @param entityId The ID of the entity to update. - */ -void entityUpdate(const entityid_t entityId); - -/** - * Disposes of an entity with the given ID. Fires all dispose callbacks before - * cleaning up components and state. - * - * @param entityId The ID of the entity to dispose of. - */ -void entityDispose(const entityid_t entityId); - -/** - * Disposes of an entity and all of its position-component descendants - * recursively. If the entity has no position component, behaves like - * entityDispose. - * - * @param entityId The root entity ID. - */ -void entityDisposeDeep(const entityid_t entityId); - -/** - * Registers an update callback, invoked each time entityUpdate is called. - * - * @param entityId The entity to register on. - * @param callback The function to call. - */ -void entityUpdateAdd( - const entityid_t entityId, - const entitycallback_t callback, - const componentid_t componentId, - void *user -); - -/** - * Removes a previously registered update callback. - * - * @param entityId The entity to remove from. - * @param callback The function to remove. - */ -void entityUpdateRemove( - const entityid_t entityId, - const entitycallback_t callback -); - -/** - * Registers a dispose callback, invoked at the start of entityDispose before - * any component or state cleanup. - * - * @param entityId The entity to register on. - * @param callback The function to call. - */ -void entityDisposeAdd( - const entityid_t entityId, - const entitycallback_t callback, - const componentid_t componentId, - void *user -); - -/** - * Removes a previously registered dispose callback. - * - * @param entityId The entity to remove from. - * @param callback The function to remove. - */ -void entityDisposeRemove( - const entityid_t entityId, - const entitycallback_t callback -); \ No newline at end of file diff --git a/src/dusk/entity/entitybase.h b/src/dusk/entity/entitybase.h deleted file mode 100644 index 5ac19bda..00000000 --- a/src/dusk/entity/entitybase.h +++ /dev/null @@ -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" - -#define ENTITY_COUNT_MAX 64 -#define ENTITY_COMPONENT_COUNT_MAX 16 - -#define ENTITY_ID_INVALID 0xFF -#define COMPONENT_ID_INVALID 0xFF - -typedef uint8_t entityid_t; -typedef uint8_t componentid_t; -typedef uint16_t componentindex_t; \ No newline at end of file diff --git a/src/dusk/entity/entitymanager.c b/src/dusk/entity/entitymanager.c deleted file mode 100644 index 838ad389..00000000 --- a/src/dusk/entity/entitymanager.c +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entitymanager.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "console/console.h" - -entitymanager_t ENTITY_MANAGER; - -void entityManagerInit(void) { - memoryZero(&ENTITY_MANAGER, sizeof(entitymanager_t)); - memorySet( - ENTITY_MANAGER.entitiesWithComponent, COMPONENT_ID_INVALID, - sizeof(componentid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX - ); - - consolePrint( - "Entity Manager size: %zu bytes (%.2f KB)", - sizeof(entitymanager_t), - sizeof(entitymanager_t) / 1024.0f - ); -} - -entityid_t entityManagerAdd() { - for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) { - if((ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) != 0) continue; - entityInit(i); - return i; - } - assertUnreachable("No more entity IDs available"); - return ENTITY_ID_INVALID; -} - -void entityManagerUpdate(void) { - entityid_t i = 0; - while(i < ENTITY_COUNT_MAX) { - if((ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) != 0) { - entityUpdate(i); - } - i++; - } -} - -void entityManagerDispose(void) { - for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) { - if((ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) == 0) continue; - entityDispose(i); - } -} \ No newline at end of file diff --git a/src/dusk/entity/entitymanager.h b/src/dusk/entity/entitymanager.h deleted file mode 100644 index 0952561e..00000000 --- a/src/dusk/entity/entitymanager.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entity.h" - -typedef struct { - entity_t entities[ENTITY_COUNT_MAX]; - component_t components[ENTITY_COUNT_MAX * ENTITY_COMPONENT_COUNT_MAX]; - componentid_t entitiesWithComponent[COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX]; -} entitymanager_t; - -extern entitymanager_t ENTITY_MANAGER; - -/** - * Initializes the entity manager. - */ -void entityManagerInit(void); - -/** - * Adds / Reserves a new entity ID. - * - * @return The new entity ID. - */ -entityid_t entityManagerAdd(); - -/** - * Updates all active entities. - */ -void entityManagerUpdate(void); - -/** - * Disposes of the entity manager, in turn freeing all entities and components. - */ -void entityManagerDispose(void); \ No newline at end of file diff --git a/src/dusk/overworld/facingdir.c b/src/dusk/overworld/facingdir.c deleted file mode 100644 index 8b99c82d..00000000 --- a/src/dusk/overworld/facingdir.c +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "facingdir.h" - -void facingDirToVec2(facingdir_t facing, vec2 dest) { - switch(facing) { - case FACING_DIR_UP: dest[0] = 0.0f; dest[1] = -1.0f; return; - case FACING_DIR_LEFT: dest[0] = -1.0f; dest[1] = 0.0f; return; - case FACING_DIR_RIGHT: dest[0] = 1.0f; dest[1] = 0.0f; return; - default: dest[0] = 0.0f; dest[1] = 1.0f; return; - } -} diff --git a/src/dusk/overworld/facingdir.h b/src/dusk/overworld/facingdir.h deleted file mode 100644 index e84981f5..00000000 --- a/src/dusk/overworld/facingdir.h +++ /dev/null @@ -1,28 +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" - -typedef enum { - FACING_DIR_DOWN = 0, - FACING_DIR_UP = 1, - FACING_DIR_LEFT = 2, - FACING_DIR_RIGHT = 3, - FACING_DIR_SOUTH = FACING_DIR_DOWN, - FACING_DIR_NORTH = FACING_DIR_UP, - FACING_DIR_WEST = FACING_DIR_LEFT, - FACING_DIR_EAST = FACING_DIR_RIGHT, -} facingdir_t; - -/** - * Converts a facing direction to a normalized XZ vec2. - * - * @param facing The facing direction. - * @param dest Output vec2 - [0] is X, [1] is Z. - */ -void facingDirToVec2(facingdir_t facing, vec2 dest); diff --git a/src/dusk/overworld/map.c b/src/dusk/overworld/map.c deleted file mode 100644 index cc63c2c3..00000000 --- a/src/dusk/overworld/map.c +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "map.h" -#include "assert/assert.h" -#include "asset/assetfile.h" -#include "util/memory.h" -#include "util/string.h" -#include "console/console.h" - -map_t MAP; - -chunkindex_t mapChunkRelToIndex( - chunkunit_t rx, - chunkunit_t ry, - chunkunit_t rz -) { - return (chunkindex_t)( - rz * (MAP_CHUNKS_WIDE * MAP_CHUNKS_HIGH) + - ry * MAP_CHUNKS_WIDE + - rx - ); -} - -void mapInit(void) { - memoryZero(&MAP, sizeof(map_t)); -} - -errorret_t mapLoad(const char_t *handle) { - assertStrLenMin(handle, 1, "Map handle cannot be empty"); - assertStrLenMax(handle, MAP_HANDLE_MAX - 1, "Map handle too long"); - - if(mapIsLoaded()) mapDispose(); - - memoryZero(&MAP, sizeof(map_t)); - stringCopy(MAP.handle, handle, MAP_HANDLE_MAX); - - char_t path[ASSET_FILE_NAME_MAX]; - stringFormat(path, sizeof(path), "maps/%s/init.js", handle); - - consolePrint("Map loaded: %s", handle); - errorOk(); -} - -bool_t mapIsLoaded(void) { - return MAP.loaded; -} - -errorret_t mapPositionSet(tilepos_t tilePos) { - assertTrue(MAP.chunkTileWidth > 0, "chunkTileWidth not set"); - assertTrue(MAP.chunkTileHeight > 0, "chunkTileHeight not set"); - assertTrue(MAP.chunkTileDepth > 0, "chunkTileDepth not set"); - - // Convert tile position to chunk-space window origin. - chunkpos_t newPos = { - .x = (chunkunit_t)(tilePos.x / MAP.chunkTileWidth), - .y = (chunkunit_t)(tilePos.y / MAP.chunkTileHeight), - .z = (chunkunit_t)(tilePos.z / MAP.chunkTileDepth), - }; - - if(MAP.loaded && chunkPosEqual(MAP.chunkPosition, newPos)) errorOk(); - - // Categorise existing chunks as remaining or freed. - chunkindex_t remaining[MAP_CHUNKS_COUNT]; - chunkindex_t freed[MAP_CHUNKS_COUNT]; - chunkindex_t remainingCount = 0; - chunkindex_t freedCount = 0; - - for(chunkindex_t i = 0; i < MAP_CHUNKS_COUNT; i++) { - mapchunk_t *chunk = &MAP.chunks[i]; - chunkpos_t p = chunk->position; - - bool_t stays = MAP.loaded && - p.x >= newPos.x && p.x < newPos.x + MAP_CHUNKS_WIDE && - p.y >= newPos.y && p.y < newPos.y + MAP_CHUNKS_HIGH && - p.z >= newPos.z && p.z < newPos.z + MAP_CHUNKS_DEEP; - - if(stays) { - remaining[remainingCount++] = i; - } else { - if(MAP.loaded) mapChunkUnload(chunk); - freed[freedCount++] = i; - } - } - - // Build chunkOrder for the new window, loading into freed slots as needed. - chunkindex_t orderIndex = 0; - for(chunkunit_t zOff = 0; zOff < MAP_CHUNKS_DEEP; zOff++) { - for(chunkunit_t yOff = 0; yOff < MAP_CHUNKS_HIGH; yOff++) { - for(chunkunit_t xOff = 0; xOff < MAP_CHUNKS_WIDE; xOff++) { - chunkpos_t target = { - .x = (chunkunit_t)(newPos.x + xOff), - .y = (chunkunit_t)(newPos.y + yOff), - .z = (chunkunit_t)(newPos.z + zOff), - }; - - // Check if the target chunk is already loaded. - chunkindex_t poolIdx = -1; - for(chunkindex_t r = 0; r < remainingCount; r++) { - if(chunkPosEqual(MAP.chunks[remaining[r]].position, target)) { - poolIdx = remaining[r]; - break; - } - } - - // Otherwise recycle a freed slot. - if(poolIdx == -1) { - poolIdx = freed[--freedCount]; - MAP.chunks[poolIdx].position = target; - errorChain(mapChunkLoad(&MAP.chunks[poolIdx])); - } - - MAP.chunkOrder[orderIndex++] = &MAP.chunks[poolIdx]; - }}} - - MAP.chunkPosition = newPos; - MAP.loaded = true; - errorOk(); -} - -mapchunk_t *mapGetChunkAt(chunkpos_t pos) { - if(!MAP.loaded) return NULL; - chunkpos_t p = MAP.chunkPosition; - if( - pos.x < p.x || pos.x >= p.x + MAP_CHUNKS_WIDE || - pos.y < p.y || pos.y >= p.y + MAP_CHUNKS_HIGH || - pos.z < p.z || pos.z >= p.z + MAP_CHUNKS_DEEP - ) return NULL; - chunkindex_t idx = mapChunkRelToIndex( - pos.x - p.x, pos.y - p.y, pos.z - p.z - ); - return MAP.chunkOrder[idx]; -} - -errorret_t mapUpdate(void) { - if(!MAP.loaded) errorOk(); - errorOk(); -} - -void mapDispose(void) { - consolePrint("Map disposing: %s", MAP.handle); - if(!MAP.loaded) return; - for(chunkindex_t i = 0; i < MAP_CHUNKS_COUNT; i++) { - mapChunkUnload(&MAP.chunks[i]); - } - MAP.loaded = false; -} diff --git a/src/dusk/overworld/map.h b/src/dusk/overworld/map.h deleted file mode 100644 index 6abfa784..00000000 --- a/src/dusk/overworld/map.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "mapchunk.h" -#include "error/error.h" - -#define MAP_NAME_MAX 64 -#define MAP_HANDLE_MAX 32 -#define MAP_CHUNKS_WIDE 3 -#define MAP_CHUNKS_HIGH 3 -#define MAP_CHUNKS_DEEP 2 -#define MAP_CHUNKS_COUNT (MAP_CHUNKS_WIDE * MAP_CHUNKS_HIGH * MAP_CHUNKS_DEEP) - -typedef struct { - char_t name[MAP_NAME_MAX]; - char_t handle[MAP_HANDLE_MAX]; - uint16_t chunkTileWidth; - uint16_t chunkTileHeight; - uint16_t chunkTileDepth; - mapchunk_t chunks[MAP_CHUNKS_COUNT]; - mapchunk_t *chunkOrder[MAP_CHUNKS_COUNT]; - chunkpos_t chunkPosition; - bool_t loaded; -} map_t; - -extern map_t MAP; - -/** - * Initializes the map, zeroing all state. - */ -void mapInit(void); - -/** - * Prepares the map for use with the given handle. If a map is already loaded - * it is disposed first. Chunk positions are not set until mapPositionSet is - * called. - * - * @param handle Short identifier for this map (max MAP_HANDLE_MAX - 1 chars). - * @return An error code. - */ -errorret_t mapLoad(const char_t *handle); - -/** - * Returns true if a map is currently loaded. - * - * @return true if a map is loaded, false otherwise. - */ -bool_t mapIsLoaded(void); - -/** - * Converts a chunk position relative to the window origin to its pool index. - * - * @param rx Relative chunk X offset within the window. - * @param ry Relative chunk Y offset within the window. - * @param rz Relative chunk Z offset within the window. - * @return The flat pool index for that relative position. - */ -chunkindex_t mapChunkRelToIndex( - chunkunit_t rx, - chunkunit_t ry, - chunkunit_t rz -); - -/** - * Slides the loaded chunk window so its origin is the chunk that contains - * the given tile-space position. Only the delta is loaded/unloaded. - * - * @param tilePos Tile-space position of the new window origin. - * @return An error code. - */ -errorret_t mapPositionSet(tilepos_t tilePos); - -/** - * Updates the map each frame. - * - * @return An error code. - */ -errorret_t mapUpdate(void); - -/** - * Disposes the map, unloading all chunks. - */ -void mapDispose(void); - -/** - * Returns the chunk at the given absolute chunk-space position, or NULL if - * it is outside the currently loaded window. - * - * @param pos Absolute chunk-space position to look up. - * @return Pointer to the chunk, or NULL if not loaded. - */ -mapchunk_t *mapGetChunkAt(chunkpos_t pos); diff --git a/src/dusk/overworld/mapchunk.c b/src/dusk/overworld/mapchunk.c deleted file mode 100644 index a7fa78f3..00000000 --- a/src/dusk/overworld/mapchunk.c +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "mapchunk.h" -#include "map.h" -#include "entity/entitymanager.h" -#include "util/memory.h" -#include "util/string.h" -#include "asset/asset.h" -#include "console/console.h" - -errorret_t mapChunkLoad(mapchunk_t *chunk) { - chunk->entityCount = 0; - memoryZero(chunk->entities, sizeof(chunk->entities)); - - if(MAP.handle[0] == '\0') errorOk(); - - char_t path[ASSET_FILE_NAME_MAX]; - stringFormat( - path, sizeof(path), - "maps/%s/chunks/%d_%d_%d.js", - MAP.handle, - (int)chunk->position.x, - (int)chunk->position.y, - (int)chunk->position.z - ); - - if(!assetFileExists(path)) errorOk(); - - consolePrint( - "Chunk loaded: %s [%d,%d,%d]", - path, - (int)chunk->position.x, - (int)chunk->position.y, - (int)chunk->position.z - ); - errorOk(); -} - -void mapChunkUnload(mapchunk_t *chunk) { - consolePrint( - "Chunk unloading: [%d,%d,%d]", - (int)chunk->position.x, - (int)chunk->position.y, - (int)chunk->position.z - ); - - for(uint8_t i = 0; i < chunk->entityCount; i++) { - entityDispose(chunk->entities[i]); - } - chunk->entityCount = 0; -} diff --git a/src/dusk/overworld/mapchunk.h b/src/dusk/overworld/mapchunk.h deleted file mode 100644 index 7513220f..00000000 --- a/src/dusk/overworld/mapchunk.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "maptypes.h" -#include "entity/entitybase.h" -#include "error/error.h" - -#define MAP_CHUNK_ENTITY_COUNT_MAX 64 - -typedef struct { - chunkpos_t position; - entityid_t entities[MAP_CHUNK_ENTITY_COUNT_MAX]; - uint8_t entityCount; -} mapchunk_t; - -/** - * Loads content into a chunk at its current position. - * - * @param chunk The chunk to load. - * @return An error code. - */ -errorret_t mapChunkLoad(mapchunk_t *chunk); - -/** - * Disposes all entities owned by the chunk and resets its state. - * - * @param chunk The chunk to unload. - */ -void mapChunkUnload(mapchunk_t *chunk); diff --git a/src/dusk/overworld/maptypes.c b/src/dusk/overworld/maptypes.c deleted file mode 100644 index 0ae48514..00000000 --- a/src/dusk/overworld/maptypes.c +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "maptypes.h" - -bool_t chunkPosEqual(chunkpos_t a, chunkpos_t b) { - return a.x == b.x && a.y == b.y && a.z == b.z; -} diff --git a/src/dusk/overworld/maptypes.h b/src/dusk/overworld/maptypes.h deleted file mode 100644 index 3815c41c..00000000 --- a/src/dusk/overworld/maptypes.h +++ /dev/null @@ -1,30 +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" - -typedef int16_t chunkunit_t; -typedef int16_t chunkindex_t; -typedef int32_t tileunit_t; - -typedef struct { - chunkunit_t x, y, z; -} chunkpos_t; - -typedef struct { - tileunit_t x, y, z; -} tilepos_t; - -/** - * Checks if two chunk positions are equal. - * - * @param a The first chunk position. - * @param b The second chunk position. - * @return true if the positions are equal, false otherwise. - */ -bool_t chunkPosEqual(chunkpos_t a, chunkpos_t b); diff --git a/src/dusk/physics/CMakeLists.txt b/src/dusk/physics/CMakeLists.txt deleted file mode 100644 index cc51815a..00000000 --- a/src/dusk/physics/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - physicsmanager.c - physicsworld.c - physicstest.c -) \ No newline at end of file diff --git a/src/dusk/physics/physicsbodytype.h b/src/dusk/physics/physicsbodytype.h deleted file mode 100644 index 7c5ffd4e..00000000 --- a/src/dusk/physics/physicsbodytype.h +++ /dev/null @@ -1,27 +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" - -typedef enum { - /** - * Never moves. Acts as an immovable collision surface. - */ - PHYSICS_BODY_STATIC, - - /** - * Simulated by the world step: gravity, forces, and collision response. - */ - PHYSICS_BODY_DYNAMIC, - - /** - * Moved programmatically via physicsWorldMoveBody; collides but is not - * driven by the simulation. Typical use: player character controller. - */ - PHYSICS_BODY_KINEMATIC -} physicsbodytype_t; diff --git a/src/dusk/physics/physicsmanager.c b/src/dusk/physics/physicsmanager.c deleted file mode 100644 index a25b43fe..00000000 --- a/src/dusk/physics/physicsmanager.c +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "physicsmanager.h" -#include "time/time.h" - -void physicsManagerInit(void) { - physicsWorldInit(); -} - -void physicsManagerUpdate() { - #if DUSK_TIME_DYNAMIC - if(TIME.dynamicUpdate) return; // Don't update on dynamic updates. - #endif - - physicsWorldStep(TIME.delta); -} \ No newline at end of file diff --git a/src/dusk/physics/physicsmanager.h b/src/dusk/physics/physicsmanager.h deleted file mode 100644 index 9492a46b..00000000 --- a/src/dusk/physics/physicsmanager.h +++ /dev/null @@ -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 "physicsworld.h" - -/** - * Initializes the physics manager. - */ -void physicsManagerInit(void); - -/** - * Advances the physics simulation. - */ -void physicsManagerUpdate(); \ No newline at end of file diff --git a/src/dusk/physics/physicsshape.h b/src/dusk/physics/physicsshape.h deleted file mode 100644 index bcf22014..00000000 --- a/src/dusk/physics/physicsshape.h +++ /dev/null @@ -1,46 +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" - -typedef enum { - PHYSICS_SHAPE_CUBE, - PHYSICS_SHAPE_SPHERE, - PHYSICS_SHAPE_CAPSULE, - PHYSICS_SHAPE_PLANE -} physicshapetype_t; - -typedef struct { - vec3 halfExtents; -} physicsshapecube_t; - -typedef struct { - float_t radius; -} physicsshapesphere_t; - -typedef struct { - float_t radius; - float_t halfHeight; -} physicsshapecapsule_t; - -typedef struct { - vec3 normal; - float_t distance; -} physicsshapeplane_t; - -typedef union { - physicsshapecube_t cube; - physicsshapesphere_t sphere; - physicsshapecapsule_t capsule; - physicsshapeplane_t plane; -} physicsshapedata_t; - -typedef struct { - physicshapetype_t type; - physicsshapedata_t data; -} physicsshape_t; diff --git a/src/dusk/physics/physicstest.c b/src/dusk/physics/physicstest.c deleted file mode 100644 index 7a22bd89..00000000 --- a/src/dusk/physics/physicstest.c +++ /dev/null @@ -1,402 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "physicstest.h" - -bool_t physicsTestAabbVsAabb( - const vec3 ac, const vec3 ah, - const vec3 bc, const vec3 bh, - vec3 outNormal, float_t *outDepth -) { - float_t dx = ac[0] - bc[0]; - float_t dy = ac[1] - bc[1]; - float_t dz = ac[2] - bc[2]; - - float_t px = (ah[0] + bh[0]) - fabsf(dx); - float_t py = (ah[1] + bh[1]) - fabsf(dy); - float_t pz = (ah[2] + bh[2]) - fabsf(dz); - - if(px <= 0.0f || py <= 0.0f || pz <= 0.0f) return false; - - outNormal[0] = outNormal[1] = outNormal[2] = 0.0f; - if(px < py && px < pz) { - *outDepth = px; - outNormal[0] = dx >= 0.0f ? 1.0f : -1.0f; - } else if(py < pz) { - *outDepth = py; - outNormal[1] = dy >= 0.0f ? 1.0f : -1.0f; - } else { - *outDepth = pz; - outNormal[2] = dz >= 0.0f ? 1.0f : -1.0f; - } - return true; -} - -bool_t physicsTestSphereVsSphere( - const vec3 ac, const float_t ar, - const vec3 bc, const float_t br, - vec3 outNormal, float_t *outDepth -) { - vec3 diff; - glm_vec3_sub((float_t *)ac, (float_t *)bc, diff); - float_t dist2 = glm_vec3_norm2(diff); - float_t sumR = ar + br; - - if(dist2 >= sumR * sumR) return false; - - float_t dist = sqrtf(dist2); - *outDepth = sumR - dist; - - if(dist > 1e-6f) { - glm_vec3_scale(diff, 1.0f / dist, outNormal); - } else { - outNormal[0] = 0.0f; - outNormal[1] = 1.0f; - outNormal[2] = 0.0f; - } - return true; -} - -bool_t physicsTestSphereVsAabb( - const vec3 sc, const float_t sr, - const vec3 ac, const vec3 ah, - vec3 outNormal, float_t *outDepth -) { - vec3 closest = { - glm_clamp(sc[0], ac[0] - ah[0], ac[0] + ah[0]), - glm_clamp(sc[1], ac[1] - ah[1], ac[1] + ah[1]), - glm_clamp(sc[2], ac[2] - ah[2], ac[2] + ah[2]) - }; - - vec3 diff; - glm_vec3_sub((float_t *)sc, closest, diff); - float_t dist2 = glm_vec3_norm2(diff); - - bool_t inside = (dist2 < 1e-10f); - if(!inside && dist2 >= sr * sr) return false; - - if(!inside) { - float_t dist = sqrtf(dist2); - *outDepth = sr - dist; - glm_vec3_scale(diff, 1.0f / dist, outNormal); - } else { - float_t faces[6] = { - (ac[0] + ah[0]) - sc[0], - sc[0] - (ac[0] - ah[0]), - (ac[1] + ah[1]) - sc[1], - sc[1] - (ac[1] - ah[1]), - (ac[2] + ah[2]) - sc[2], - sc[2] - (ac[2] - ah[2]) - }; - const float_t normals[6][3] = { - {1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1} - }; - int_t mi = 0; - for(int_t k = 1; k < 6; k++) { - if(faces[k] < faces[mi]) mi = k; - } - *outDepth = sr + faces[mi]; - outNormal[0] = normals[mi][0]; - outNormal[1] = normals[mi][1]; - outNormal[2] = normals[mi][2]; - } - return true; -} - -bool_t physicsTestSphereVsPlane( - const vec3 sc, const float_t sr, - const vec3 pn, const float_t pd, - vec3 outNormal, float_t *outDepth -) { - float_t signedDist = glm_vec3_dot((float_t *)pn, (float_t *)sc) - pd; - *outDepth = sr - signedDist; - if(*outDepth <= 0.0f) return false; - glm_vec3_copy((float_t *)pn, outNormal); - return true; -} - -bool_t physicsTestAabbVsPlane( - const vec3 ac, const vec3 ah, - const vec3 pn, const float_t pd, - vec3 outNormal, float_t *outDepth -) { - float_t proj = fabsf(pn[0] * ah[0]) - + fabsf(pn[1] * ah[1]) - + fabsf(pn[2] * ah[2]); - float_t signedDist = glm_vec3_dot((float_t *)pn, (float_t *)ac) - pd; - *outDepth = proj - signedDist; - if(*outDepth <= 0.0f) return false; - glm_vec3_copy((float_t *)pn, outNormal); - return true; -} - -void physicsTestClosestPointOnSegment( - const vec3 a, const vec3 b, const vec3 p, vec3 out -) { - vec3 ab, ap; - glm_vec3_sub((float_t *)b, (float_t *)a, ab); - glm_vec3_sub((float_t *)p, (float_t *)a, ap); - float_t denom = glm_vec3_dot(ab, ab); - float_t t = (denom > 1e-10f) - ? glm_clamp(glm_vec3_dot(ap, ab) / denom, 0.0f, 1.0f) - : 0.0f; - glm_vec3_lerp((float_t *)a, (float_t *)b, t, out); -} - -void physicsTestClosestPointsBetweenSegments( - const vec3 a1, const vec3 b1, - const vec3 a2, const vec3 b2, - vec3 outP1, vec3 outP2 -) { - vec3 d1, d2, r; - glm_vec3_sub((float_t *)b1, (float_t *)a1, d1); - glm_vec3_sub((float_t *)b2, (float_t *)a2, d2); - glm_vec3_sub((float_t *)a1, (float_t *)a2, r); - - float_t a = glm_vec3_dot(d1, d1); - float_t e = glm_vec3_dot(d2, d2); - float_t f = glm_vec3_dot(d2, r); - float_t s, t; - - if(a <= 1e-10f && e <= 1e-10f) { - glm_vec3_copy((float_t *)a1, outP1); - glm_vec3_copy((float_t *)a2, outP2); - return; - } - if(a <= 1e-10f) { - t = 0.0f; - s = glm_clamp(f / e, 0.0f, 1.0f); - } else { - float_t c = glm_vec3_dot(d1, r); - if(e <= 1e-10f) { - s = 0.0f; - t = glm_clamp(-c / a, 0.0f, 1.0f); - } else { - float_t b = glm_vec3_dot(d1, d2); - float_t denom = a * e - b * b; - t = (fabsf(denom) > 1e-10f) - ? glm_clamp((b * f - c * e) / denom, 0.0f, 1.0f) - : 0.0f; - s = glm_clamp((b * t + f) / e, 0.0f, 1.0f); - t = glm_clamp((b * s - c) / a, 0.0f, 1.0f); - } - } - glm_vec3_lerp((float_t *)a1, (float_t *)b1, t, outP1); - glm_vec3_lerp((float_t *)a2, (float_t *)b2, s, outP2); -} - -bool_t physicsTestCapsuleVsSphere( - const vec3 cc, const float_t cr, const float_t chh, - const vec3 sc, const float_t sr, - vec3 outNormal, float_t *outDepth -) { - vec3 capA = { cc[0], cc[1] - chh, cc[2] }; - vec3 capB = { cc[0], cc[1] + chh, cc[2] }; - vec3 closest; - physicsTestClosestPointOnSegment(capA, capB, sc, closest); - return physicsTestSphereVsSphere( - closest, cr, sc, sr, outNormal, outDepth - ); -} - -bool_t physicsTestCapsuleVsAabb( - const vec3 cc, const float_t cr, const float_t chh, - const vec3 ac, const vec3 ah, - vec3 outNormal, float_t *outDepth -) { - vec3 capA = { cc[0], cc[1] - chh, cc[2] }; - vec3 capB = { cc[0], cc[1] + chh, cc[2] }; - vec3 closest; - physicsTestClosestPointOnSegment(capA, capB, ac, closest); - return physicsTestSphereVsAabb( - closest, cr, ac, ah, outNormal, outDepth - ); -} - -bool_t physicsTestCapsuleVsPlane( - const vec3 cc, const float_t cr, const float_t chh, - const vec3 pn, const float_t pd, - vec3 outNormal, float_t *outDepth -) { - vec3 capA = { cc[0], cc[1] - chh, cc[2] }; - vec3 capB = { cc[0], cc[1] + chh, cc[2] }; - float_t da = glm_vec3_dot((float_t *)pn, capA) - pd; - float_t db = glm_vec3_dot((float_t *)pn, capB) - pd; - float_t minDist = (da < db) ? da : db; - *outDepth = cr - minDist; - if(*outDepth <= 0.0f) return false; - glm_vec3_copy((float_t *)pn, outNormal); - return true; -} - -bool_t physicsTestCapsuleVsCapsule( - const vec3 c1, const float_t r1, const float_t hh1, - const vec3 c2, const float_t r2, const float_t hh2, - vec3 outNormal, float_t *outDepth -) { - vec3 a1 = { c1[0], c1[1] - hh1, c1[2] }; - vec3 b1 = { c1[0], c1[1] + hh1, c1[2] }; - vec3 a2 = { c2[0], c2[1] - hh2, c2[2] }; - vec3 b2 = { c2[0], c2[1] + hh2, c2[2] }; - vec3 p1, p2; - physicsTestClosestPointsBetweenSegments(a1, b1, a2, b2, p1, p2); - return physicsTestSphereVsSphere(p1, r1, p2, r2, outNormal, outDepth); -} - -bool_t physicsTestDispatch( - const vec3 aPos, const physicsshape_t aShape, - const vec3 bPos, const physicsshape_t bShape, - vec3 outNormal, float_t *outDepth -) { - physicshapetype_t ta = aShape.type; - physicshapetype_t tb = bShape.type; - - if(tb == PHYSICS_SHAPE_PLANE) { - const float_t *pn = bShape.data.plane.normal; - const float_t pd = bShape.data.plane.distance; - switch(ta) { - case PHYSICS_SHAPE_CUBE: - return physicsTestAabbVsPlane( - aPos, aShape.data.cube.halfExtents, - pn, pd, outNormal, outDepth - ); - case PHYSICS_SHAPE_SPHERE: - return physicsTestSphereVsPlane( - aPos, aShape.data.sphere.radius, - pn, pd, outNormal, outDepth - ); - case PHYSICS_SHAPE_CAPSULE: - return physicsTestCapsuleVsPlane( - aPos, - aShape.data.capsule.radius, - aShape.data.capsule.halfHeight, - pn, pd, outNormal, outDepth - ); - default: - return false; - } - } - - if(ta == PHYSICS_SHAPE_PLANE) { - vec3 tmp; float_t d; - if(!physicsTestDispatch( - bPos, bShape, aPos, aShape, tmp, &d - )) return false; - glm_vec3_scale(tmp, -1.0f, outNormal); - *outDepth = d; - return true; - } - - switch(ta) { - case PHYSICS_SHAPE_CUBE: { - const float_t *ac = aPos; - const float_t *ah = aShape.data.cube.halfExtents; - switch(tb) { - case PHYSICS_SHAPE_CUBE: - return physicsTestAabbVsAabb( - ac, ah, - bPos, bShape.data.cube.halfExtents, - outNormal, outDepth - ); - case PHYSICS_SHAPE_SPHERE: { - vec3 tmp; float_t d; - if(!physicsTestSphereVsAabb( - bPos, bShape.data.sphere.radius, - ac, ah, tmp, &d - )) return false; - glm_vec3_scale(tmp, -1.0f, outNormal); - *outDepth = d; - return true; - } - case PHYSICS_SHAPE_CAPSULE: { - vec3 tmp; float_t d; - if(!physicsTestCapsuleVsAabb( - bPos, - bShape.data.capsule.radius, - bShape.data.capsule.halfHeight, - ac, ah, tmp, &d - )) return false; - glm_vec3_scale(tmp, -1.0f, outNormal); - *outDepth = d; - return true; - } - default: return false; - } - } - - case PHYSICS_SHAPE_SPHERE: { - const float_t sr = aShape.data.sphere.radius; - switch(tb) { - case PHYSICS_SHAPE_CUBE: - return physicsTestSphereVsAabb( - aPos, sr, - bPos, bShape.data.cube.halfExtents, - outNormal, outDepth - ); - case PHYSICS_SHAPE_SPHERE: - return physicsTestSphereVsSphere( - aPos, sr, - bPos, bShape.data.sphere.radius, - outNormal, outDepth - ); - case PHYSICS_SHAPE_CAPSULE: { - vec3 tmp; float_t d; - if(!physicsTestCapsuleVsSphere( - bPos, - bShape.data.capsule.radius, - bShape.data.capsule.halfHeight, - aPos, sr, tmp, &d - )) return false; - glm_vec3_scale(tmp, -1.0f, outNormal); - *outDepth = d; - return true; - } - default: return false; - } - } - - case PHYSICS_SHAPE_CAPSULE: { - const float_t cr = aShape.data.capsule.radius; - const float_t chh = aShape.data.capsule.halfHeight; - switch(tb) { - case PHYSICS_SHAPE_CUBE: - return physicsTestCapsuleVsAabb( - aPos, cr, chh, - bPos, bShape.data.cube.halfExtents, - outNormal, outDepth - ); - case PHYSICS_SHAPE_SPHERE: - return physicsTestCapsuleVsSphere( - aPos, cr, chh, - bPos, bShape.data.sphere.radius, - outNormal, outDepth - ); - case PHYSICS_SHAPE_CAPSULE: - return physicsTestCapsuleVsCapsule( - aPos, cr, chh, - bPos, - bShape.data.capsule.radius, - bShape.data.capsule.halfHeight, - outNormal, outDepth - ); - default: return false; - } - } - - default: return false; - } -} - -bool_t physicsTestShapeVsShape( - const vec3 aPos, const physicsshape_t aShape, - const vec3 bPos, const physicsshape_t bShape, - vec3 outNormal, float_t *outDepth -) { - return physicsTestDispatch( - aPos, aShape, bPos, bShape, outNormal, outDepth - ); -} diff --git a/src/dusk/physics/physicstest.h b/src/dusk/physics/physicstest.h deleted file mode 100644 index 3c02697c..00000000 --- a/src/dusk/physics/physicstest.h +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "physicsshape.h" - -/** - * Tests overlap between two axis-aligned bounding boxes. - * outNormal points from B toward A. - * - * @param ac Center of AABB A. - * @param ah Half-extents of AABB A. - * @param bc Center of AABB B. - * @param bh Half-extents of AABB B. - * @param outNormal Push-out normal (B toward A). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestAabbVsAabb( - const vec3 ac, const vec3 ah, - const vec3 bc, const vec3 bh, - vec3 outNormal, float_t *outDepth -); - -/** - * Tests overlap between two spheres. - * outNormal points from B toward A. - * - * @param ac Center of sphere A. - * @param ar Radius of sphere A. - * @param bc Center of sphere B. - * @param br Radius of sphere B. - * @param outNormal Push-out normal (B toward A). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestSphereVsSphere( - const vec3 ac, const float_t ar, - const vec3 bc, const float_t br, - vec3 outNormal, float_t *outDepth -); - -/** - * Tests overlap between a sphere and an axis-aligned bounding box. - * outNormal points from the AABB toward the sphere. - * - * @param sc Center of the sphere. - * @param sr Radius of the sphere. - * @param ac Center of the AABB. - * @param ah Half-extents of the AABB. - * @param outNormal Push-out normal (AABB toward sphere). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestSphereVsAabb( - const vec3 sc, const float_t sr, - const vec3 ac, const vec3 ah, - vec3 outNormal, float_t *outDepth -); - -/** - * Tests overlap between a sphere and an infinite plane. - * outNormal equals the plane normal (pointing away from the surface). - * - * @param sc Center of the sphere. - * @param sr Radius of the sphere. - * @param pn Plane normal (unit vector, world-space). - * @param pd Plane offset: dot(pn, surfacePoint) == pd. - * @param outNormal Push-out normal (equals pn). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestSphereVsPlane( - const vec3 sc, const float_t sr, - const vec3 pn, const float_t pd, - vec3 outNormal, float_t *outDepth -); - -/** - * Tests overlap between an AABB and an infinite plane. - * outNormal equals the plane normal. - * - * @param ac Center of the AABB. - * @param ah Half-extents of the AABB. - * @param pn Plane normal (unit vector, world-space). - * @param pd Plane offset (see physicsTestSphereVsPlane). - * @param outNormal Push-out normal (equals pn). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestAabbVsPlane( - const vec3 ac, const vec3 ah, - const vec3 pn, const float_t pd, - vec3 outNormal, float_t *outDepth -); - -/** - * Finds the closest point on segment [a, b] to query point p. - * - * @param a Start of the segment. - * @param b End of the segment. - * @param p Query point. - * @param out Receives the closest point on [a, b] to p. - */ -void physicsTestClosestPointOnSegment( - const vec3 a, const vec3 b, const vec3 p, vec3 out -); - -/** - * Finds the closest points between two line segments. - * - * @param a1 Start of segment 1. - * @param b1 End of segment 1. - * @param a2 Start of segment 2. - * @param b2 End of segment 2. - * @param outP1 Receives the closest point on segment 1. - * @param outP2 Receives the closest point on segment 2. - */ -void physicsTestClosestPointsBetweenSegments( - const vec3 a1, const vec3 b1, - const vec3 a2, const vec3 b2, - vec3 outP1, vec3 outP2 -); - -/** - * Tests overlap between a Y-axis-aligned capsule and a sphere. - * outNormal points from the sphere toward the capsule. - * - * @param cc Center of the capsule. - * @param cr Radius of the capsule. - * @param chh Half-height of the capsule's cylindrical segment. - * @param sc Center of the sphere. - * @param sr Radius of the sphere. - * @param outNormal Push-out normal (sphere toward capsule). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestCapsuleVsSphere( - const vec3 cc, const float_t cr, const float_t chh, - const vec3 sc, const float_t sr, - vec3 outNormal, float_t *outDepth -); - -/** - * Tests overlap between a Y-axis-aligned capsule and an AABB. - * outNormal points from the AABB toward the capsule. - * - * @param cc Center of the capsule. - * @param cr Radius of the capsule. - * @param chh Half-height of the capsule's cylindrical segment. - * @param ac Center of the AABB. - * @param ah Half-extents of the AABB. - * @param outNormal Push-out normal (AABB toward capsule). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestCapsuleVsAabb( - const vec3 cc, const float_t cr, const float_t chh, - const vec3 ac, const vec3 ah, - vec3 outNormal, float_t *outDepth -); - -/** - * Tests overlap between a Y-axis-aligned capsule and an infinite plane. - * outNormal equals the plane normal. - * - * @param cc Center of the capsule. - * @param cr Radius of the capsule. - * @param chh Half-height of the capsule's cylindrical segment. - * @param pn Plane normal (unit vector, world-space). - * @param pd Plane offset (see physicsTestSphereVsPlane). - * @param outNormal Push-out normal (equals pn). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestCapsuleVsPlane( - const vec3 cc, const float_t cr, const float_t chh, - const vec3 pn, const float_t pd, - vec3 outNormal, float_t *outDepth -); - -/** - * Tests overlap between two Y-axis-aligned capsules. - * outNormal points from capsule B toward capsule A. - * - * @param c1 Center of capsule A. - * @param r1 Radius of capsule A. - * @param hh1 Half-height of capsule A's cylindrical segment. - * @param c2 Center of capsule B. - * @param r2 Radius of capsule B. - * @param hh2 Half-height of capsule B's cylindrical segment. - * @param outNormal Push-out normal (B toward A). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestCapsuleVsCapsule( - const vec3 c1, const float_t r1, const float_t hh1, - const vec3 c2, const float_t r2, const float_t hh2, - vec3 outNormal, float_t *outDepth -); - -/** - * Routes a shape-pair collision test to the correct primitive. - * When A is a plane, delegates with swapped arguments and negates - * the resulting normal. outNormal points from B toward A. - * - * @param aPos Position of shape A. - * @param aShape Shape descriptor of A. - * @param bPos Position of shape B. - * @param bShape Shape descriptor of B. - * @param outNormal Push-out normal (B toward A). - * @param outDepth Penetration depth. - * @return true if overlapping. - */ -bool_t physicsTestDispatch( - const vec3 aPos, const physicsshape_t aShape, - const vec3 bPos, const physicsshape_t bShape, - vec3 outNormal, float_t *outDepth -); - -/** - * Tests for collision between two shapes. Returns true if they - * overlap, and if so, outputs the push-out normal and depth. - * - * outNormal always points from shape B toward shape A, so adding - * (outNormal * outDepth) to A's position separates the two shapes. - * - * @param aPos Position of shape A. - * @param aShape Shape descriptor of A. - * @param bPos Position of shape B. - * @param bShape Shape descriptor of B. - * @param outNormal Push-out normal, pointing from B toward A. - * @param outDepth Penetration depth (positive when overlapping). - * @return true if the shapes overlap, false otherwise. - */ -bool_t physicsTestShapeVsShape( - const vec3 aPos, - const physicsshape_t aShape, - const vec3 bPos, - const physicsshape_t bShape, - vec3 outNormal, - float_t *outDepth -); diff --git a/src/dusk/physics/physicsworld.c b/src/dusk/physics/physicsworld.c deleted file mode 100644 index db3b0cb0..00000000 --- a/src/dusk/physics/physicsworld.c +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "physicsworld.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "entity/entity.h" -#include "entity/component.h" -#include "physicstest.h" - -physicsworld_t PHYSICS_WORLD; - -void physicsWorldInit() { - memoryZero(&PHYSICS_WORLD, sizeof(physicsworld_t)); - - PHYSICS_WORLD.gravity[0] = 0.0f; - PHYSICS_WORLD.gravity[1] = -9.81f; - PHYSICS_WORLD.gravity[2] = 0.0f; -} - -void physicsWorldStep(const float_t dt) { - assertTrue(dt > 0.0f, "Delta time must be positive"); - - entityid_t physEnts[ENTITY_COUNT_MAX]; - componentid_t physComps[ENTITY_COUNT_MAX]; - entityid_t physCount = componentGetEntitiesWithComponent( - COMPONENT_TYPE_PHYSICS, physEnts, physComps - ); - - /* Pre-fetch all position and physics pointers once. */ - entityposition_t *positions[ENTITY_COUNT_MAX]; - entityphysics_t *physBodies[ENTITY_COUNT_MAX]; - for(entityid_t i = 0; i < physCount; i++) { - componentid_t posComp = entityGetComponent( - physEnts[i], COMPONENT_TYPE_POSITION - ); - positions[i] = (posComp != 0xFF) - ? entityPositionGet(physEnts[i], posComp) - : NULL; - physBodies[i] = entityPhysicsGet(physEnts[i], physComps[i]); - } - - /* Phase 1: integrate dynamic bodies (gravity + velocity -> position). - * Writes directly to pos->position, matrix rebuilt at end. */ - for(entityid_t i = 0; i < physCount; i++) { - if(!positions[i]) continue; - entityphysics_t *phys = physBodies[i]; - if(phys->type != PHYSICS_BODY_DYNAMIC) continue; - - phys->onGround = false; - - phys->velocity[0] += PHYSICS_WORLD.gravity[0] * phys->gravityScale * dt; - phys->velocity[1] += PHYSICS_WORLD.gravity[1] * phys->gravityScale * dt; - phys->velocity[2] += PHYSICS_WORLD.gravity[2] * phys->gravityScale * dt; - - float_t *pos = positions[i]->position; - pos[0] += phys->velocity[0] * dt; - pos[1] += phys->velocity[1] * dt; - pos[2] += phys->velocity[2] * dt; - } - - /* Phase 2: dynamic vs static/kinematic. */ - for(entityid_t i = 0; i < physCount; i++) { - if(!positions[i]) continue; - entityphysics_t *phys = physBodies[i]; - if(phys->type != PHYSICS_BODY_DYNAMIC) continue; - - float_t *pos = positions[i]->position; - - for(entityid_t j = 0; j < physCount; j++) { - if(i == j || !positions[j]) continue; - entityphysics_t *otherPhys = physBodies[j]; - if(otherPhys->type == PHYSICS_BODY_DYNAMIC) continue; - - vec3 normal; float_t depth; - if(!physicsTestShapeVsShape( - pos, phys->shape, - positions[j]->position, otherPhys->shape, - normal, &depth - )) continue; - - pos[0] += normal[0] * depth; - pos[1] += normal[1] * depth; - pos[2] += normal[2] * depth; - - float_t vn = glm_vec3_dot(phys->velocity, normal); - if(vn < 0.0f) { - phys->velocity[0] -= vn * normal[0]; - phys->velocity[1] -= vn * normal[1]; - phys->velocity[2] -= vn * normal[2]; - } - - if(normal[1] > PHYSICS_GROUND_THRESHOLD) phys->onGround = true; - } - } - - /* Phase 3: dynamic vs dynamic. */ - for(entityid_t i = 0; i < physCount; i++) { - if(!positions[i]) continue; - entityphysics_t *physA = physBodies[i]; - if(physA->type != PHYSICS_BODY_DYNAMIC) continue; - - float_t *posA = positions[i]->position; - - for(entityid_t j = i + 1; j < physCount; j++) { - if(!positions[j]) continue; - entityphysics_t *physB = physBodies[j]; - if(physB->type != PHYSICS_BODY_DYNAMIC) continue; - - float_t *posB = positions[j]->position; - - vec3 normal; float_t depth; - if(!physicsTestShapeVsShape( - posA, physA->shape, posB, physB->shape, normal, &depth - )) continue; - - posA[0] += normal[0] * depth * 0.5f; - posA[1] += normal[1] * depth * 0.5f; - posA[2] += normal[2] * depth * 0.5f; - - posB[0] -= normal[0] * depth * 0.5f; - posB[1] -= normal[1] * depth * 0.5f; - posB[2] -= normal[2] * depth * 0.5f; - - float_t v_rel = glm_vec3_dot(physA->velocity, normal) - - glm_vec3_dot(physB->velocity, normal); - if(v_rel < 0.0f) { - physA->velocity[0] -= v_rel * normal[0]; - physA->velocity[1] -= v_rel * normal[1]; - physA->velocity[2] -= v_rel * normal[2]; - physB->velocity[0] += v_rel * normal[0]; - physB->velocity[1] += v_rel * normal[1]; - physB->velocity[2] += v_rel * normal[2]; - } - - if( normal[1] > PHYSICS_GROUND_THRESHOLD) physA->onGround = true; - if(-normal[1] > PHYSICS_GROUND_THRESHOLD) physB->onGround = true; - } - } - - /* Rebuild transforms for all dynamic bodies once, after all phases. */ - for(entityid_t i = 0; i < physCount; i++) { - if(!positions[i]) continue; - if(physBodies[i]->type != PHYSICS_BODY_DYNAMIC) continue; - entityPositionRebuild(positions[i]); - } -} diff --git a/src/dusk/physics/physicsworld.h b/src/dusk/physics/physicsworld.h deleted file mode 100644 index a06dc5c8..00000000 --- a/src/dusk/physics/physicsworld.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "physics/physicsshape.h" -#include "physics/physicsbodytype.h" - -#define PHYSICS_GROUND_THRESHOLD 0.707f - -typedef struct { - vec3 gravity; -} physicsworld_t; - -extern physicsworld_t PHYSICS_WORLD; - -/** - * Initializes the physics world. - */ -void physicsWorldInit(void); - -/** - * Steps the physics simulation forward. - * - * @param dt The time delta in seconds since the last step. - */ -void physicsWorldStep(const float_t dt); \ No newline at end of file diff --git a/src/dusk/rpg/CMakeLists.txt b/src/dusk/rpg/CMakeLists.txt new file mode 100644 index 00000000..5af4c304 --- /dev/null +++ b/src/dusk/rpg/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + rpg.c + rpgcamera.c + rpgtextbox.c +) + +# Subdirs +add_subdirectory(cutscene) +add_subdirectory(entity) +add_subdirectory(overworld) + +add_subdirectory(story) +add_subdirectory(item) \ No newline at end of file diff --git a/src/dusk/script/module/entity/CMakeLists.txt b/src/dusk/rpg/cutscene/CMakeLists.txt similarity index 59% rename from src/dusk/script/module/entity/CMakeLists.txt rename to src/dusk/rpg/cutscene/CMakeLists.txt index a5b59035..9c927691 100644 --- a/src/dusk/script/module/entity/CMakeLists.txt +++ b/src/dusk/rpg/cutscene/CMakeLists.txt @@ -1,13 +1,14 @@ -# Copyright (c) 2026 Dominic Masters +# Copyright (c) 2025 Dominic Masters # # This software is released under the MIT License. # https://opensource.org/licenses/MIT +# Sources target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - modulecomponent.c - moduleentity.c + cutscenesystem.c + cutscenemode.c ) # Subdirs -add_subdirectory(component) +add_subdirectory(item) \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/cutscene.h b/src/dusk/rpg/cutscene/cutscene.h new file mode 100644 index 00000000..7df18ad6 --- /dev/null +++ b/src/dusk/rpg/cutscene/cutscene.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/cutscene/item/cutsceneitem.h" + +typedef struct cutscene_s { + const cutsceneitem_t *items; + uint8_t itemCount; +} cutscene_t; \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/cutscenemode.c b/src/dusk/rpg/cutscene/cutscenemode.c new file mode 100644 index 00000000..39c10b3e --- /dev/null +++ b/src/dusk/rpg/cutscene/cutscenemode.c @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpg/cutscene/cutscenesystem.h" + +bool_t cutsceneModeIsInputAllowed() { + switch(CUTSCENE_SYSTEM.mode) { + case CUTSCENE_MODE_FULL_FREEZE: + case CUTSCENE_MODE_INPUT_FREEZE: + return false; + + default: + return true; + } +} \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/cutscenemode.h b/src/dusk/rpg/cutscene/cutscenemode.h new file mode 100644 index 00000000..cf20a3d3 --- /dev/null +++ b/src/dusk/rpg/cutscene/cutscenemode.h @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef enum { + CUTSCENE_MODE_NONE, + CUTSCENE_MODE_FULL_FREEZE, + CUTSCENE_MODE_INPUT_FREEZE, + CUTSCENE_MODE_GAMEPLAY +} cutscenemode_t; + +// Default mode for all cutscenes. +#define CUTSCENE_MODE_INITIAL CUTSCENE_MODE_INPUT_FREEZE + +/** + * Check if input is allowed in the current cutscene mode. + * + * @return true if input is allowed, false otherwise. + */ +bool_t cutsceneModeIsInputAllowed(); \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/cutscenesystem.c b/src/dusk/rpg/cutscene/cutscenesystem.c new file mode 100644 index 00000000..d803e05d --- /dev/null +++ b/src/dusk/rpg/cutscene/cutscenesystem.c @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "cutscenesystem.h" +#include "util/memory.h" + +cutscenesystem_t CUTSCENE_SYSTEM; + +void cutsceneSystemInit() { + memoryZero(&CUTSCENE_SYSTEM, sizeof(cutscenesystem_t)); +} + +void cutsceneSystemStartCutscene(const cutscene_t *cutscene) { + CUTSCENE_SYSTEM.scene = cutscene; + CUTSCENE_SYSTEM.mode = CUTSCENE_MODE_INITIAL; + CUTSCENE_SYSTEM.currentItem = 0xFF;// Set to 0xFF so start wraps. + cutsceneSystemNext(); +} + +void cutsceneSystemUpdate() { + if(CUTSCENE_SYSTEM.scene == NULL) return; + + const cutsceneitem_t *item = cutsceneSystemGetCurrentItem(); + cutsceneItemUpdate(item, &CUTSCENE_SYSTEM.data); +} + +void cutsceneSystemNext() { + if(CUTSCENE_SYSTEM.scene == NULL) return; + + CUTSCENE_SYSTEM.currentItem++; + + // End of the cutscene? + if( + CUTSCENE_SYSTEM.currentItem >= CUTSCENE_SYSTEM.scene->itemCount + ) { + CUTSCENE_SYSTEM.scene = NULL; + CUTSCENE_SYSTEM.currentItem = 0xFF; + CUTSCENE_SYSTEM.mode = CUTSCENE_MODE_NONE; + return; + } + + // Start item. + const cutsceneitem_t *item = cutsceneSystemGetCurrentItem(); + memset(&CUTSCENE_SYSTEM.data, 0, sizeof(CUTSCENE_SYSTEM.data)); + cutsceneItemStart(item, &CUTSCENE_SYSTEM.data); +} + +const cutsceneitem_t * cutsceneSystemGetCurrentItem() { + if(CUTSCENE_SYSTEM.scene == NULL) return NULL; + + return &CUTSCENE_SYSTEM.scene->items[CUTSCENE_SYSTEM.currentItem]; +} \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/cutscenesystem.h b/src/dusk/rpg/cutscene/cutscenesystem.h new file mode 100644 index 00000000..7c3242a9 --- /dev/null +++ b/src/dusk/rpg/cutscene/cutscenesystem.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "cutscene.h" +#include "cutscenemode.h" + +typedef struct { + const cutscene_t *scene; + uint8_t currentItem; + + // Data (used by the current item). + cutsceneitemdata_t data; + cutscenemode_t mode; +} cutscenesystem_t; + +extern cutscenesystem_t CUTSCENE_SYSTEM; + +/** + * Initialize the cutscene system. + */ +void cutsceneSystemInit(); + +/** + * Start a cutscene. + * + * @param cutscene Pointer to the cutscene to start. + */ +void cutsceneSystemStartCutscene(const cutscene_t *cutscene); + +/** + * Advance to the next item in the cutscene. + */ +void cutsceneSystemNext(); + +/** + * Update the cutscene system for one frame. + */ +void cutsceneSystemUpdate(); + +/** + * Get the current cutscene item. + * + * @return Pointer to the current cutscene item. + */ +const cutsceneitem_t * cutsceneSystemGetCurrentItem(); \ No newline at end of file diff --git a/src/dusk/entity/component/physics/CMakeLists.txt b/src/dusk/rpg/cutscene/item/CMakeLists.txt old mode 100644 new mode 100755 similarity index 73% rename from src/dusk/entity/component/physics/CMakeLists.txt rename to src/dusk/rpg/cutscene/item/CMakeLists.txt index f3848009..6b3e271a --- a/src/dusk/entity/component/physics/CMakeLists.txt +++ b/src/dusk/rpg/cutscene/item/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2026 Dominic Masters +# Copyright (c) 2025 Dominic Masters # # This software is released under the MIT License. # https://opensource.org/licenses/MIT @@ -6,5 +6,5 @@ # Sources target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - entityphysics.c + cutsceneitem.c ) \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/cutscenecallback.h b/src/dusk/rpg/cutscene/item/cutscenecallback.h new file mode 100644 index 00000000..494f877d --- /dev/null +++ b/src/dusk/rpg/cutscene/item/cutscenecallback.h @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef void (*cutscenecallback_t)(void); \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.c b/src/dusk/rpg/cutscene/item/cutsceneitem.c new file mode 100644 index 00000000..18a8b582 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpg/cutscene/cutscenesystem.h" +#include "input/input.h" +#include "time/time.h" + +void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { + switch(item->type) { + case CUTSCENE_ITEM_TYPE_TEXT: { + rpgTextboxShow( + item->text.position, + item->text.text + ); + break; + } + + case CUTSCENE_ITEM_TYPE_WAIT: + data->wait = item->wait; + break; + + case CUTSCENE_ITEM_TYPE_CALLBACK: + if(item->callback != NULL) item->callback(); + break; + + case CUTSCENE_ITEM_TYPE_CUTSCENE: + if(item->cutscene != NULL) cutsceneSystemStartCutscene(item->cutscene); + break; + + default: + break; + } +} + +void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) { + switch(item->type) { + case CUTSCENE_ITEM_TYPE_TEXT: + if(rpgTextboxIsVisible()) return; + cutsceneSystemNext(); + break; + + case CUTSCENE_ITEM_TYPE_WAIT: + data->wait -= TIME.delta; + if(data->wait <= 0) cutsceneSystemNext(); + break; + + default: + break; + } +} \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.h b/src/dusk/rpg/cutscene/item/cutsceneitem.h new file mode 100644 index 00000000..f632518d --- /dev/null +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.h @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "cutscenewait.h" +#include "cutscenecallback.h" +#include "cutscenetext.h" + +typedef struct cutscene_s cutscene_t; + +typedef enum { + CUTSCENE_ITEM_TYPE_NULL, + CUTSCENE_ITEM_TYPE_TEXT, + CUTSCENE_ITEM_TYPE_CALLBACK, + CUTSCENE_ITEM_TYPE_WAIT, + CUTSCENE_ITEM_TYPE_CUTSCENE +} cutsceneitemtype_t; + +typedef struct cutsceneitem_s { + cutsceneitemtype_t type; + + // Arguments/Data that will be used when this item is invoked. + union { + cutscenetext_t text; + cutscenecallback_t callback; + cutscenewait_t wait; + const cutscene_t *cutscene; + }; +} cutsceneitem_t; + +typedef union { + cutscenewaitdata_t wait; +} cutsceneitemdata_t; + +/** + * Start the given cutscene item. + * + * @param item The cutscene item to start. + * @param data The cutscene item data storage. + */ +void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data); + +/** + * Tick the given cutscene item (one frame). + * + * @param item The cutscene item to tick. + * @param data The cutscene item data storage. + */ +void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data); \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/cutscenetext.h b/src/dusk/rpg/cutscene/item/cutscenetext.h new file mode 100644 index 00000000..3ad07f29 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/cutscenetext.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/rpgtextbox.h" + +typedef struct { + char_t text[RPG_TEXTBOX_MAX_CHARS]; + rpgtextboxpos_t position; +} cutscenetext_t; \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/cutscenewait.h b/src/dusk/rpg/cutscene/item/cutscenewait.h new file mode 100644 index 00000000..9d590df2 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/cutscenewait.h @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef float_t cutscenewait_t; +typedef float_t cutscenewaitdata_t; \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/scene/testcutscene.h b/src/dusk/rpg/cutscene/scene/testcutscene.h new file mode 100755 index 00000000..bcdffb21 --- /dev/null +++ b/src/dusk/rpg/cutscene/scene/testcutscene.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/cutscene/cutscenesystem.h" + +static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = { + { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene.", .position = RPG_TEXTBOX_POS_BOTTOM } }, + { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f }, + { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between.", .position = RPG_TEXTBOX_POS_TOP } }, +}; + +static const cutscene_t TEST_CUTSCENE_ONE = { + .items = TEST_CUTSCENE_ONE_ITEMS, + .itemCount = sizeof(TEST_CUTSCENE_ONE_ITEMS) / sizeof(cutsceneitem_t) +}; + +static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = { + { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }, + { .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE }, +}; + +static const cutscene_t TEST_CUTSCENE = { + .items = TEST_CUTSCENE_TWO_ITEMS, + .itemCount = sizeof(TEST_CUTSCENE_TWO_ITEMS) / sizeof(cutsceneitem_t) +}; \ No newline at end of file diff --git a/src/dusk/entity/CMakeLists.txt b/src/dusk/rpg/entity/CMakeLists.txt similarity index 58% rename from src/dusk/entity/CMakeLists.txt rename to src/dusk/rpg/entity/CMakeLists.txt index 150936ec..f6e4cda5 100644 --- a/src/dusk/entity/CMakeLists.txt +++ b/src/dusk/rpg/entity/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2026 Dominic Masters -# +# Copyright (c) 2025 Dominic Masters +# # This software is released under the MIT License. # https://opensource.org/licenses/MIT @@ -7,9 +7,8 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC entity.c - entitymanager.c - component.c -) - -# Subdirs -add_subdirectory(component) \ No newline at end of file + entityanim.c + npc.c + player.c + entitydir.c +) \ No newline at end of file diff --git a/src/dusk/rpg/entity/entity.c b/src/dusk/rpg/entity/entity.c new file mode 100644 index 00000000..4b8c0ffa --- /dev/null +++ b/src/dusk/rpg/entity/entity.c @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "entity.h" +#include "assert/assert.h" +#include "util/memory.h" +#include "time/time.h" +#include "util/math.h" +#include "rpg/cutscene/cutscenemode.h" +#include "rpg/overworld/map.h" +#include "rpg/overworld/tile.h" + +entity_t ENTITIES[ENTITY_COUNT]; + +void entityInit(entity_t *entity, const entitytype_t type) { + assertNotNull(entity, "Entity pointer cannot be NULL"); + assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type"); + assertTrue(type != ENTITY_TYPE_NULL, "Cannot have NULL entity type"); + assertTrue( + entity >= ENTITIES && entity < ENTITIES + ENTITY_COUNT, + "Entity pointer is out of bounds" + ); + + memoryZero(entity, sizeof(entity_t)); + entity->id = (uint8_t)(entity - ENTITIES); + entity->type = type; + + if(ENTITY_CALLBACKS[type].init != NULL) ENTITY_CALLBACKS[type].init(entity); +} + +void entityUpdate(entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); + assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type"); + assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type"); + + // What state is the entity in? + if(entity->animation != ENTITY_ANIM_IDLE) { + // Entity is mid animation, tick it (down). + entity->animTime -= TIME.delta; + if(entity->animTime <= 0) { + entity->animation = ENTITY_ANIM_IDLE; + entity->animTime = 0; + } + return; + } + + // Movement code. + if( + cutsceneModeIsInputAllowed() && + ENTITY_CALLBACKS[entity->type].movement != NULL + ) { + ENTITY_CALLBACKS[entity->type].movement(entity); + } +} + +void entityTurn(entity_t *entity, const entitydir_t direction) { + entity->direction = direction; + entity->animation = ENTITY_ANIM_TURN; + entity->animTime = ENTITY_ANIM_TURN_DURATION; +} + +void entityWalk(entity_t *entity, const entitydir_t direction) { + // TODO: Animation, delay, etc. + entity->direction = direction; + + // Where are we moving? + worldpos_t newPos = entity->position; + { + worldunits_t relX, relY; + entityDirGetRelative(direction, &relX, &relY); + newPos.x += relX; + newPos.y += relY; + } + + // Get tile under foot + tile_t tileCurrent = mapGetTile(entity->position); + tile_t tileNew = mapGetTile(newPos); + bool_t fall = false; + bool_t raise = false; + + // Are we walking up a ramp? + if( + tileIsRamp(tileCurrent) && + ( + // Can only walk UP the direction the ramp faces. + (direction+TILE_SHAPE_RAMP_SOUTH) == tileCurrent || + // If diagonal ramp, can go up one of two ways only. + ( + ( + tileCurrent == TILE_SHAPE_RAMP_SOUTHEAST && + (direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_EAST) + ) || + ( + tileCurrent == TILE_SHAPE_RAMP_SOUTHWEST && + (direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_WEST) + ) || + ( + tileCurrent == TILE_SHAPE_RAMP_NORTHEAST && + (direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_EAST) + ) || + ( + tileCurrent == TILE_SHAPE_RAMP_NORTHWEST && + (direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_WEST) + ) + ) + // Must be able to walk up. + ) + ) { + tileNew = TILE_SHAPE_NULL;// Force check for ramp above. + worldpos_t abovePos = newPos; + abovePos.z += 1; + tile_t tileAbove = mapGetTile(abovePos); + + if(tileAbove != TILE_SHAPE_NULL && tileIsWalkable(tileAbove)) { + // We can go up the ramp. + raise = true; + } + } else if(tileNew == TILE_SHAPE_NULL && newPos.z > 0) { + // Falling down? + worldpos_t belowPos = newPos; + belowPos.z -= 1; + tile_t tileBelow = mapGetTile(belowPos); + if( + tileBelow != TILE_SHAPE_NULL && + tileIsRamp(tileBelow) && + ( + // This handles regular cardinal ramps + (entityDirGetOpposite(direction)+TILE_SHAPE_RAMP_SOUTH) == tileBelow || + // This handles diagonal ramps + ( + ( + tileBelow == TILE_SHAPE_RAMP_SOUTHEAST && + (direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_WEST) + ) || + ( + tileBelow == TILE_SHAPE_RAMP_SOUTHWEST && + (direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_EAST) + ) || + ( + tileBelow == TILE_SHAPE_RAMP_NORTHEAST && + (direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_WEST) + ) || + ( + tileBelow == TILE_SHAPE_RAMP_NORTHWEST && + (direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_EAST) + ) + ) + ) + ) { + // We will fall to this tile. + fall = true; + } + } + + // Can we walk here? + if(!raise && !fall && !tileIsWalkable(tileNew)) return;// Blocked + + // Entity in way? + entity_t *other = ENTITIES; + do { + if(other == entity) continue; + if(other->type == ENTITY_TYPE_NULL) continue; + if(!worldPosIsEqual(other->position, newPos)) continue; + return;// Blocked + } while(++other, other < &ENTITIES[ENTITY_COUNT]); + + entity->lastPosition = entity->position; + entity->position = newPos; + entity->animation = ENTITY_ANIM_WALK; + entity->animTime = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking + + if(raise) { + entity->position.z += 1; + } else if(fall) { + entity->position.z -= 1; + } +} + +entity_t * entityGetAt(const worldpos_t position) { + entity_t *ent = ENTITIES; + do { + if(ent->type == ENTITY_TYPE_NULL) continue; + if(!worldPosIsEqual(ent->position, position)) continue; + return ent; + } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); + + return NULL; +} + +uint8_t entityGetAvailable() { + entity_t *ent = ENTITIES; + do { + if(ent->type == ENTITY_TYPE_NULL) return ent - ENTITIES; + } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); + + return 0xFF; +} \ No newline at end of file diff --git a/src/dusk/rpg/entity/entity.h b/src/dusk/rpg/entity/entity.h new file mode 100644 index 00000000..258b54f2 --- /dev/null +++ b/src/dusk/rpg/entity/entity.h @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "entitydir.h" +#include "entityanim.h" +#include "entitytype.h" +#include "npc.h" + +typedef struct map_s map_t; + +typedef struct entity_s { + uint8_t id; + entitytype_t type; + entitytypedata_t data; + + // Movement + entitydir_t direction; + worldpos_t position; + worldpos_t lastPosition; + + entityanim_t animation; + float_t animTime; +} entity_t; + +extern entity_t ENTITIES[ENTITY_COUNT]; + +/** + * Initializes an entity structure. + * + * @param entity Pointer to the entity structure to initialize. + * @param type The type of the entity. + */ +void entityInit(entity_t *entity, const entitytype_t type); + +/** + * Updates an entity. + * + * @param entity Pointer to the entity structure to update. + */ +void entityUpdate(entity_t *entity); + +/** + * Turn an entity to face a new direction. + * + * @param entity Pointer to the entity to turn. + * @param direction The direction to face. + */ +void entityTurn(entity_t *entity, const entitydir_t direction); + +/** + * Make an entity walk in a direction. + * + * @param entity Pointer to the entity to make walk. + * @param direction The direction to walk in. + */ +void entityWalk(entity_t *entity, const entitydir_t direction); + +/** + * Gets the entity at a specific world position. + * + * @param map Pointer to the map to check. + * @param pos The world position to check. + * @return Pointer to the entity at the position, or NULL if none. + */ +entity_t *entityGetAt(const worldpos_t pos); + +/** + * Gets an available entity index. + * + * @return The index of an available entity, or 0xFF if none are available. + */ +uint8_t entityGetAvailable(); \ No newline at end of file diff --git a/src/dusk/rpg/entity/entityanim.c b/src/dusk/rpg/entity/entityanim.c new file mode 100644 index 00000000..d5730f3f --- /dev/null +++ b/src/dusk/rpg/entity/entityanim.c @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "entityanim.h" + diff --git a/src/dusk/rpg/entity/entityanim.h b/src/dusk/rpg/entity/entityanim.h new file mode 100644 index 00000000..42f7d40a --- /dev/null +++ b/src/dusk/rpg/entity/entityanim.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +#define ENTITY_ANIM_TURN_DURATION 0.06f +#define ENTITY_ANIM_WALK_DURATION 0.1f + +typedef enum { + ENTITY_ANIM_IDLE, + ENTITY_ANIM_TURN, + ENTITY_ANIM_WALK, +} entityanim_t; \ No newline at end of file diff --git a/src/dusk/rpg/entity/entitydir.c b/src/dusk/rpg/entity/entitydir.c new file mode 100644 index 00000000..e272877a --- /dev/null +++ b/src/dusk/rpg/entity/entitydir.c @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "entitydir.h" +#include "assert/assert.h" + +entitydir_t entityDirGetOpposite(const entitydir_t dir) { + switch(dir) { + case ENTITY_DIR_NORTH: return ENTITY_DIR_SOUTH; + case ENTITY_DIR_SOUTH: return ENTITY_DIR_NORTH; + case ENTITY_DIR_EAST: return ENTITY_DIR_WEST; + case ENTITY_DIR_WEST: return ENTITY_DIR_EAST; + default: return dir; + } +} + +void entityDirGetRelative( + const entitydir_t from, + worldunits_t *outX, + worldunits_t *outY +) { + assertValidEntityDir(from, "Invalid direction provided"); + assertNotNull(outX, "Output X pointer cannot be NULL"); + assertNotNull(outY, "Output Y pointer cannot be NULL"); + + switch(from) { + case ENTITY_DIR_NORTH: + *outX = 0; + *outY = -1; + break; + + case ENTITY_DIR_EAST: + *outX = 1; + *outY = 0; + break; + + case ENTITY_DIR_SOUTH: + *outX = 0; + *outY = 1; + break; + + case ENTITY_DIR_WEST: + *outX = -1; + *outY = 0; + break; + } +} \ No newline at end of file diff --git a/src/dusk/rpg/entity/entitydir.h b/src/dusk/rpg/entity/entitydir.h new file mode 100644 index 00000000..2eefab88 --- /dev/null +++ b/src/dusk/rpg/entity/entitydir.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/overworld/worldpos.h" + +typedef enum { + ENTITY_DIR_NORTH, + ENTITY_DIR_EAST, + ENTITY_DIR_SOUTH, + ENTITY_DIR_WEST, + ENTITY_DIR_UP = ENTITY_DIR_NORTH, + ENTITY_DIR_DOWN = ENTITY_DIR_SOUTH, + ENTITY_DIR_LEFT = ENTITY_DIR_WEST, + ENTITY_DIR_RIGHT = ENTITY_DIR_EAST, +} entitydir_t; + +/** + * Gets the opposite direction of a given direction. + * + * @param dir The direction to get the opposite of. + * @return entitydir_t The opposite direction. + */ +entitydir_t entityDirGetOpposite(const entitydir_t dir); + +/** + * Asserts a given direction is valid. + * + * @param dir The direction to validate. + * @param msg The message to display if the assertion fails. + */ +#define assertValidEntityDir(dir, msg) \ + assertTrue( \ + (dir) == ENTITY_DIR_NORTH || \ + (dir) == ENTITY_DIR_EAST || \ + (dir) == ENTITY_DIR_SOUTH || \ + (dir) == ENTITY_DIR_WEST, \ + msg \ + ) + +/** + * Gets the relative x and y offsets for a given direction. + * + * @param dir The direction to get offsets for. + * @param relX Pointer to store the relative x offset. + * @param relY Pointer to store the relative y offset. + */ +void entityDirGetRelative( + const entitydir_t dir, worldunits_t *relX, worldunits_t *relY +); \ No newline at end of file diff --git a/src/dusk/rpg/entity/entitytype.h b/src/dusk/rpg/entity/entitytype.h new file mode 100644 index 00000000..fb67f33c --- /dev/null +++ b/src/dusk/rpg/entity/entitytype.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/entity/player.h" +#include "npc.h" + +typedef enum { + ENTITY_TYPE_NULL, + + ENTITY_TYPE_PLAYER, + ENTITY_TYPE_NPC, + + ENTITY_TYPE_COUNT +} entitytype_enum_t; + +typedef uint8_t entitytype_t; + +typedef union { + player_t player; + npc_t npc; +} entitytypedata_t; + +typedef struct { + /** + * Initialization callback for the entity type. + * @param entity Pointer to the entity to initialize. + */ + void (*init)(entity_t *entity); + + /** + * Movement callback for the entity type. + * @param entity Pointer to the entity to move. + */ + void (*movement)(entity_t *entity); + + /** + * Interaction callback for the entity type. + * @param player Pointer to the player entity. + * @param entity Pointer to the entity to interact with. + * @return True if the entity handled the interaction, false otherwise. + */ + bool_t (*interact)(entity_t *player, entity_t *entity); +} entitycallback_t; + +static const entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = { + [ENTITY_TYPE_NULL] = { NULL }, + + [ENTITY_TYPE_PLAYER] = { + .init = playerInit, + .movement = playerInput + }, + + [ENTITY_TYPE_NPC] = { + .init = npcInit, + .movement = npcMovement, + .interact = npcInteract + } +}; \ No newline at end of file diff --git a/src/dusk/rpg/entity/npc.c b/src/dusk/rpg/entity/npc.c new file mode 100644 index 00000000..ec6548bd --- /dev/null +++ b/src/dusk/rpg/entity/npc.c @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "entity.h" +#include "assert/assert.h" + +#include "rpg/cutscene/scene/testcutscene.h" +#include "rpg/rpgtextbox.h" + +void npcInit(entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); +} + +void npcMovement(entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); +} + +bool_t npcInteract(entity_t *player, entity_t *npc) { + assertNotNull(player, "Player entity pointer cannot be NULL"); + assertNotNull(npc, "NPC entity pointer cannot be NULL"); + + cutsceneSystemStartCutscene(&TEST_CUTSCENE); + + // rpgTextboxShow(RPG_TEXTBOX_POS_BOTTOM, "Hello World!"); + + return false; +}; \ No newline at end of file diff --git a/src/dusk/rpg/entity/npc.h b/src/dusk/rpg/entity/npc.h new file mode 100644 index 00000000..df36f15e --- /dev/null +++ b/src/dusk/rpg/entity/npc.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef struct entity_s entity_t; + +typedef struct { + void *nothing; +} npc_t; + +/** + * Initializes an NPC entity. + * + * @param entity Pointer to the entity structure to initialize. + */ +void npcInit(entity_t *entity); + +/** + * Updates an NPC entity. + * + * @param entity Pointer to the entity structure to update. + */ +void npcMovement(entity_t *entity); + +/** + * Handles interaction with an NPC entity. + * + * @param player Pointer to the player entity. + * @param npc Pointer to the NPC entity. + */ +bool_t npcInteract(entity_t *player, entity_t *npc); \ No newline at end of file diff --git a/src/dusk/rpg/entity/player.c b/src/dusk/rpg/entity/player.c new file mode 100644 index 00000000..cf33d562 --- /dev/null +++ b/src/dusk/rpg/entity/player.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "entity.h" +#include "assert/assert.h" +#include "rpg/rpgcamera.h" +#include "util/memory.h" +#include "time/time.h" + +void playerInit(entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); +} + +void playerInput(entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); + + // Turn + const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP; + do { + if(!inputIsDown(dirMap->action)) continue; + if(entity->direction == dirMap->direction) continue; + return entityTurn(entity, dirMap->direction); + } while((++dirMap)->action != 0xFF); + + // Walk + dirMap = PLAYER_INPUT_DIR_MAP; + do { + if(!inputIsDown(dirMap->action)) continue; + if(entity->direction != dirMap->direction) continue; + return entityWalk(entity, dirMap->direction); + } while((++dirMap)->action != 0xFF); + + // Interaction + if(inputPressed(INPUT_ACTION_ACCEPT)) { + worldunit_t x, y, z; + { + worldunits_t relX, relY; + entityDirGetRelative(entity->direction, &relX, &relY); + x = entity->position.x + relX; + y = entity->position.y + relY; + z = entity->position.z; + } + + entity_t *interact = entityGetAt((worldpos_t){ x, y, z }); + if(interact != NULL && ENTITY_CALLBACKS[interact->type].interact != NULL) { + if(ENTITY_CALLBACKS[interact->type].interact(entity, interact)) return; + } + } + +} \ No newline at end of file diff --git a/src/dusk/rpg/entity/player.h b/src/dusk/rpg/entity/player.h new file mode 100644 index 00000000..8c10f31c --- /dev/null +++ b/src/dusk/rpg/entity/player.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "input/input.h" + +typedef struct entity_s entity_t; + +typedef struct { + void *nothing; +} player_t; + +typedef struct { + inputaction_t action; + entitydir_t direction; +} playerinputdirmap_t; + +static const playerinputdirmap_t PLAYER_INPUT_DIR_MAP[] = { + { INPUT_ACTION_UP, ENTITY_DIR_NORTH }, + { INPUT_ACTION_DOWN, ENTITY_DIR_SOUTH }, + { INPUT_ACTION_LEFT, ENTITY_DIR_WEST }, + { INPUT_ACTION_RIGHT, ENTITY_DIR_EAST }, + + { 0xFF, 0xFF } +}; + +/** + * Initializes a player entity. + * + * @param entity Pointer to the entity structure to initialize. + */ +void playerInit(entity_t *entity); + +/** + * Handles movement logic for the player entity. + * + * @param entity Pointer to the player entity structure. + */ +void playerInput(entity_t *entity); \ No newline at end of file diff --git a/src/dusk/item/CMakeLists.txt b/src/dusk/rpg/item/CMakeLists.txt similarity index 87% rename from src/dusk/item/CMakeLists.txt rename to src/dusk/rpg/item/CMakeLists.txt index 4f5606e0..40cfb7c9 100644 --- a/src/dusk/item/CMakeLists.txt +++ b/src/dusk/rpg/item/CMakeLists.txt @@ -15,6 +15,6 @@ dusk_run_python( dusk_item_csv_defs tools.item.csv --csv ${CMAKE_CURRENT_SOURCE_DIR}/item.csv - --output ${DUSK_GENERATED_HEADERS_DIR}/item/item.h + --output ${DUSK_GENERATED_HEADERS_DIR}/rpg/item/item.h ) add_dependencies(${DUSK_LIBRARY_TARGET_NAME} dusk_item_csv_defs) \ No newline at end of file diff --git a/src/dusk/item/backpack.c b/src/dusk/rpg/item/backpack.c similarity index 100% rename from src/dusk/item/backpack.c rename to src/dusk/rpg/item/backpack.c diff --git a/src/dusk/item/backpack.h b/src/dusk/rpg/item/backpack.h similarity index 100% rename from src/dusk/item/backpack.h rename to src/dusk/rpg/item/backpack.h diff --git a/src/dusk/item/inventory.c b/src/dusk/rpg/item/inventory.c similarity index 100% rename from src/dusk/item/inventory.c rename to src/dusk/rpg/item/inventory.c diff --git a/src/dusk/item/inventory.h b/src/dusk/rpg/item/inventory.h similarity index 99% rename from src/dusk/item/inventory.h rename to src/dusk/rpg/item/inventory.h index a8fdc2d5..a56d93f4 100644 --- a/src/dusk/item/inventory.h +++ b/src/dusk/rpg/item/inventory.h @@ -6,7 +6,7 @@ */ #pragma once -#include "item/item.h" +#include "rpg/item/item.h" #define ITEM_STACK_QUANTITY_MAX UINT8_MAX diff --git a/src/dusk/item/item.csv b/src/dusk/rpg/item/item.csv similarity index 100% rename from src/dusk/item/item.csv rename to src/dusk/rpg/item/item.csv diff --git a/src/dusk/overworld/CMakeLists.txt b/src/dusk/rpg/overworld/CMakeLists.txt similarity index 64% rename from src/dusk/overworld/CMakeLists.txt rename to src/dusk/rpg/overworld/CMakeLists.txt index 49ec3703..b697bf8a 100644 --- a/src/dusk/overworld/CMakeLists.txt +++ b/src/dusk/rpg/overworld/CMakeLists.txt @@ -1,12 +1,13 @@ -# Copyright (c) 2026 Dominic Masters +# Copyright (c) 2025 Dominic Masters # # This software is released under the MIT License. # https://opensource.org/licenses/MIT +# Sources target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - facingdir.c - maptypes.c + chunk.c map.c - mapchunk.c -) + worldpos.c + tile.c +) \ No newline at end of file diff --git a/src/dusk/rpg/overworld/chunk.c b/src/dusk/rpg/overworld/chunk.c new file mode 100644 index 00000000..19a9999e --- /dev/null +++ b/src/dusk/rpg/overworld/chunk.c @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "chunk.h" + +uint32_t chunkGetTileIndex(const chunkpos_t position) { + return ( + (position.z * CHUNK_WIDTH * CHUNK_HEIGHT) + + (position.y * CHUNK_WIDTH) + + position.x + ); +} + +bool_t chunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b) { + return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); +} \ No newline at end of file diff --git a/src/dusk/rpg/overworld/chunk.h b/src/dusk/rpg/overworld/chunk.h new file mode 100644 index 00000000..a81fcb4d --- /dev/null +++ b/src/dusk/rpg/overworld/chunk.h @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/overworld/tile.h" +#include "worldpos.h" +#include "display/mesh/quad.h" +#include "display/spritebatch/spritebatch.h" + +// #define CHUNK_MESH_COUNT_MAX 3 +// #define CHUNK_VERTEX_COUNT_MAX (QUAD_VERTEX_COUNT * CHUNK_MESH_COUNT_MAX) +#define CHUNK_ENTITY_COUNT_MAX 10 + +typedef struct chunk_s { + chunkpos_t position; + tile_t tiles[CHUNK_TILE_COUNT]; + + spritebatchsprite_t sprites[CHUNK_TILE_COUNT]; + uint32_t spriteCount; + + // uint8_t meshCount; + // meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX]; + // mesh_t meshes[CHUNK_MESH_COUNT_MAX]; + uint8_t entities[CHUNK_ENTITY_COUNT_MAX]; +} chunk_t; + +/** + * Gets the tile index for a tile position within a chunk. + * + * @param position The position within the chunk. + * @return The tile index within the chunk. + */ +uint32_t chunkGetTileIndex(const chunkpos_t position); + +/** + * Checks if two chunk positions are equal. + * + * @param a The first chunk position. + * @param b The second chunk position. + * @return true if equal, false otherwise. + */ +bool_t chunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b); \ No newline at end of file diff --git a/src/dusk/rpg/overworld/map.c b/src/dusk/rpg/overworld/map.c new file mode 100644 index 00000000..cf3f85c2 --- /dev/null +++ b/src/dusk/rpg/overworld/map.c @@ -0,0 +1,239 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "map.h" +#include "util/memory.h" +#include "assert/assert.h" +#include "asset/asset.h" +#include "rpg/entity/entity.h" +#include "util/string.h" + +map_t MAP; + +errorret_t mapInit() { + memoryZero(&MAP, sizeof(map_t)); + errorOk(); +} + +bool_t mapIsLoaded() { + return MAP.filePath[0] != '\0'; +} + +errorret_t mapLoad(const char_t *path, const chunkpos_t position) { + assertStrLenMin(path, 1, "Map file path cannot be empty"); + assertStrLenMax(path, MAP_FILE_PATH_MAX - 1, "Map file path too long"); + + if(stringCompare(MAP.filePath, path) == 0) { + // Same map, no need to reload + errorOk(); + } + + chunkindex_t i; + + // Unload all loaded chunks + if(mapIsLoaded()) { + for(i = 0; i < MAP_CHUNK_COUNT; i++) { + mapChunkUnload(&MAP.chunks[i]); + } + } + + // Store the map file path + stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX); + + // Determine directory path (it is dirname) + stringCopy(MAP.dirPath, path, MAP_FILE_PATH_MAX); + char_t *last = stringFindLastChar(MAP.dirPath, '/'); + if(last == NULL) errorThrow("Invalid map file path"); + + // Store filename, sans extension + stringCopy(MAP.fileName, last + 1, MAP_FILE_PATH_MAX); + *last = '\0'; // Terminate to get directory path + + last = stringFindLastChar(MAP.fileName, '.'); + if(last == NULL) errorThrow("Map file name has no extension"); + *last = '\0'; // Terminate to remove extension + + // Reset map position + MAP.chunkPosition = position; + + // Perform "initial load" + i = 0; + for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) { + for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) { + for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) { + chunk_t *chunk = &MAP.chunks[i]; + chunk->position.x = x + position.x; + chunk->position.y = y + position.y; + chunk->position.z = z + position.z; + MAP.chunkOrder[i] = chunk; + errorChain(mapChunkLoad(chunk)); + i++; + } + } + } + errorOk(); +} + +errorret_t mapPositionSet(const chunkpos_t newPos) { + if(!mapIsLoaded()) errorThrow("No map loaded"); + + const chunkpos_t curPos = MAP.chunkPosition; + if(chunkPositionIsEqual(curPos, newPos)) { + errorOk(); + } + + // Determine which chunks remain loaded + chunkindex_t chunksRemaining[MAP_CHUNK_COUNT] = {0}; + chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0}; + + uint32_t remainingCount = 0; + uint32_t freedCount = 0; + + for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { + // Will this chunk remain loaded? + chunk_t *chunk = &MAP.chunks[i]; + if( + chunk->position.x >= newPos.x && + chunk->position.x < newPos.x + MAP_CHUNK_WIDTH && + + chunk->position.y >= newPos.y && + chunk->position.y < newPos.y + MAP_CHUNK_HEIGHT && + + chunk->position.z >= newPos.z && + chunk->position.z < newPos.z + MAP_CHUNK_DEPTH + ) { + // Stays loaded + chunksRemaining[remainingCount++] = i; + continue; + } + + // Not remaining loaded + chunksFreed[freedCount++] = i; + } + + // Unload the freed chunks + for(chunkindex_t i = 0; i < freedCount; i++) { + chunk_t *chunk = &MAP.chunks[chunksFreed[i]]; + mapChunkUnload(chunk); + } + + // This can probably be optimized later, for now we check each chunk and see + // if it needs loading or not, and update the chunk order + chunkindex_t orderIndex = 0; + for(chunkunit_t zOff = 0; zOff < MAP_CHUNK_DEPTH; zOff++) { + for(chunkunit_t yOff = 0; yOff < MAP_CHUNK_HEIGHT; yOff++) { + for(chunkunit_t xOff = 0; xOff < MAP_CHUNK_WIDTH; xOff++) { + const chunkpos_t newChunkPos = { + newPos.x + xOff, newPos.y + yOff, newPos.z + zOff + }; + + // Is this chunk already loaded (was not unloaded earlier)? + chunkindex_t chunkIndex = -1; + for(chunkindex_t i = 0; i < remainingCount; i++) { + chunk_t *chunk = &MAP.chunks[chunksRemaining[i]]; + if(!chunkPositionIsEqual(chunk->position, newChunkPos)) continue; + chunkIndex = chunksRemaining[i]; + break; + } + + // Need to load this chunk + if(chunkIndex == -1) { + // Find a freed chunk to reuse + chunkIndex = chunksFreed[--freedCount]; + chunk_t *chunk = &MAP.chunks[chunkIndex]; + chunk->position = newChunkPos; + errorChain(mapChunkLoad(chunk)); + } + + MAP.chunkOrder[orderIndex++] = &MAP.chunks[chunkIndex]; + } + } + } + + // Update map position + MAP.chunkPosition = newPos; + + errorOk(); +} + +void mapUpdate() { + +} + +void mapDispose() { + for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { + mapChunkUnload(&MAP.chunks[i]); + } +} + +void mapChunkUnload(chunk_t* chunk) { + for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) { + if(chunk->entities[i] == 0xFF) break; + entity_t *entity = &ENTITIES[chunk->entities[i]]; + entity->type = ENTITY_TYPE_NULL; + } + + for(uint8_t i = 0; i < chunk->meshCount; i++) { + if(chunk->meshes[i].vertexCount == 0) continue; + meshDispose(&chunk->meshes[i]); + } +} + +errorret_t mapChunkLoad(chunk_t* chunk) { + if(!mapIsLoaded()) errorThrow("No map loaded"); + + char_t buffer[64]; + + // TODO: Can probably move this to asset load logic? + chunk->meshCount = 0; + memoryZero(chunk->meshes, sizeof(chunk->meshes)); + memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); + + // Load. + errorOk(); +} + +chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) { + if(!mapIsLoaded()) return -1; + + chunkpos_t relPos = { + position.x - MAP.chunkPosition.x, + position.y - MAP.chunkPosition.y, + position.z - MAP.chunkPosition.z + }; + + if( + relPos.x < 0 || relPos.y < 0 || relPos.z < 0 || + relPos.x >= MAP_CHUNK_WIDTH || + relPos.y >= MAP_CHUNK_HEIGHT || + relPos.z >= MAP_CHUNK_DEPTH + ) { + return -1; + } + + return chunkPosToIndex(&relPos); +} + +chunk_t* mapGetChunk(const uint8_t index) { + if(index >= MAP_CHUNK_COUNT) return NULL; + if(!mapIsLoaded()) return NULL; + return MAP.chunkOrder[index]; +} + +tile_t mapGetTile(const worldpos_t position) { + if(!mapIsLoaded()) return TILE_SHAPE_NULL; + + chunkpos_t chunkPos; + worldPosToChunkPos(&position, &chunkPos); + chunkindex_t chunkIndex = mapGetChunkIndexAt(chunkPos); + if(chunkIndex == -1) return TILE_SHAPE_NULL; + + chunk_t *chunk = mapGetChunk(chunkIndex); + assertNotNull(chunk, "Chunk pointer cannot be NULL"); + chunktileindex_t tileIndex = worldPosToChunkTileIndex(&position); + return chunk->tiles[tileIndex]; +} \ No newline at end of file diff --git a/src/dusk/rpg/overworld/map.h b/src/dusk/rpg/overworld/map.h new file mode 100644 index 00000000..8373fca0 --- /dev/null +++ b/src/dusk/rpg/overworld/map.h @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/overworld/chunk.h" + +#define MAP_FILE_PATH_MAX 128 + +typedef struct map_s { + char_t filePath[MAP_FILE_PATH_MAX]; + char_t dirPath[MAP_FILE_PATH_MAX]; + char_t fileName[MAP_FILE_PATH_MAX]; + + chunk_t chunks[MAP_CHUNK_COUNT]; + chunk_t *chunkOrder[MAP_CHUNK_COUNT]; + chunkpos_t chunkPosition; +} map_t; + +extern map_t MAP; + +/** + * Initializes the map. + * + * @return An error code. + */ +errorret_t mapInit(); + +/** + * Checks if a map is loaded. + * + * @return true if a map is loaded, false otherwise. + */ +bool_t mapIsLoaded(); + +/** + * Loads a map from the given file path. + * + * @param path The file path. + * @param position The initial chunk position. + * @return An error code. + */ +errorret_t mapLoad( + const char_t *path, + const chunkpos_t position +); + +/** + * Updates the map. + */ +void mapUpdate(); + +/** + * Disposes of the map. + */ +void mapDispose(); + +/** + * Sets the map position and updates chunks accordingly. + * + * @param newPos The new chunk position. + * @return An error code. + */ +errorret_t mapPositionSet(const chunkpos_t newPos); + +/** + * Unloads a chunk. + * + * @param chunk The chunk to unload. + */ +void mapChunkUnload(chunk_t* chunk); + +/** + * Loads a chunk. + * + * @param chunk The chunk to load. + * @return An error code. + */ +errorret_t mapChunkLoad(chunk_t* chunk); + +/** + * Gets the index of a chunk, within the world, at the given position. + * + * @param position The chunk position. + * @return The index of the chunk, or -1 if out of bounds. + */ +chunkindex_t mapGetChunkIndexAt(const chunkpos_t position); + +/** + * Gets a chunk by its index. + * + * @param chunkIndex The index of the chunk. + * @return A pointer to the chunk. + */ +chunk_t * mapGetChunk(const uint8_t chunkIndex); + +/** + * Gets the tile at the given world position. + * + * @param position The world position. + * @return The tile at that position, or TILE_NULL if the chunk is unloaded. + */ +tile_t mapGetTile(const worldpos_t position); \ No newline at end of file diff --git a/src/dusk/rpg/overworld/tile.c b/src/dusk/rpg/overworld/tile.c new file mode 100644 index 00000000..cc25881b --- /dev/null +++ b/src/dusk/rpg/overworld/tile.c @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "tile.h" + +bool_t tileIsWalkable(const tile_t tile) { + switch(tile) { + case TILE_SHAPE_NULL: + return false; + + default: + return true; + } +} + +bool_t tileIsRamp(const tile_t tile) { + switch(tile) { + case TILE_SHAPE_RAMP_NORTH: + case TILE_SHAPE_RAMP_SOUTH: + case TILE_SHAPE_RAMP_EAST: + case TILE_SHAPE_RAMP_WEST: + case TILE_SHAPE_RAMP_NORTHEAST: + case TILE_SHAPE_RAMP_NORTHWEST: + case TILE_SHAPE_RAMP_SOUTHEAST: + case TILE_SHAPE_RAMP_SOUTHWEST: + return true; + + default: + return false; + } +} \ No newline at end of file diff --git a/src/dusk/rpg/overworld/tile.h b/src/dusk/rpg/overworld/tile.h new file mode 100644 index 00000000..622f6ee2 --- /dev/null +++ b/src/dusk/rpg/overworld/tile.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/entity/entitydir.h" + + +typedef enum { + TILE_SHAPE_NULL, + + TILE_SHAPE_SOLID, + TILE_SHAPE_RAMP_NORTH, + TILE_SHAPE_RAMP_SOUTH, + TILE_SHAPE_RAMP_EAST, + TILE_SHAPE_RAMP_WEST, + TILE_SHAPE_RAMP_NORTHEAST, + TILE_SHAPE_RAMP_NORTHWEST, + TILE_SHAPE_RAMP_SOUTHEAST, + TILE_SHAPE_RAMP_SOUTHWEST, + + TILE_SHAPE_COUNT +} tile_t; + +/** + * Returns whether or not the given tile is walkable. + * + * @param tile The tile to check. + * @return bool_t True if walkable, false if not. + */ +bool_t tileIsWalkable(const tile_t tile); + +/** + * Returns whether or not the given tile is a ramp tile. + * + * @param tile The tile to check. + * @return bool_t True if ramp, false if not. + */ +bool_t tileIsRamp(const tile_t tile); \ No newline at end of file diff --git a/src/dusk/rpg/overworld/worldpos.c b/src/dusk/rpg/overworld/worldpos.c new file mode 100644 index 00000000..3cdd7100 --- /dev/null +++ b/src/dusk/rpg/overworld/worldpos.c @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "worldpos.h" +#include "assert/assert.h" + +bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b) { + return a.x == b.x && a.y == b.y && a.z == b.z; +} + +void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out) { + assertNotNull(chunkPos, "Chunk position pointer cannot be NULL"); + assertNotNull(out, "Output world position pointer cannot be NULL"); + + out->x = (worldunit_t)(chunkPos->x * CHUNK_WIDTH); + out->y = (worldunit_t)(chunkPos->y * CHUNK_HEIGHT); + out->z = (worldunit_t)(chunkPos->z * CHUNK_DEPTH); +} + +void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out) { + assertNotNull(worldPos, "World position pointer cannot be NULL"); + assertNotNull(out, "Output chunk position pointer cannot be NULL"); + + if(worldPos->x < 0) { + out->x = (chunkunit_t)((worldPos->x - (CHUNK_WIDTH - 1)) / CHUNK_WIDTH); + } else { + out->x = (chunkunit_t)(worldPos->x / CHUNK_WIDTH); + } + + if(worldPos->y < 0) { + out->y = (chunkunit_t)((worldPos->y - (CHUNK_HEIGHT - 1)) / CHUNK_HEIGHT); + } else { + out->y = (chunkunit_t)(worldPos->y / CHUNK_HEIGHT); + } + + if(worldPos->z < 0) { + out->z = (chunkunit_t)((worldPos->z - (CHUNK_DEPTH - 1)) / CHUNK_DEPTH); + } else { + out->z = (chunkunit_t)(worldPos->z / CHUNK_DEPTH); + } +} + +chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos) { + assertNotNull(worldPos, "World position pointer cannot be NULL"); + + uint8_t localX, localY, localZ; + if(worldPos->x < 0) { + localX = (uint8_t)( + (CHUNK_WIDTH - 1) - ((-worldPos->x - 1) % CHUNK_WIDTH) + ); + } else { + localX = (uint8_t)(worldPos->x % CHUNK_WIDTH); + } + + if(worldPos->y < 0) { + localY = (uint8_t)( + (CHUNK_HEIGHT - 1) - ((-worldPos->y - 1) % CHUNK_HEIGHT) + ); + } else { + localY = (uint8_t)(worldPos->y % CHUNK_HEIGHT); + } + + if(worldPos->z < 0) { + localZ = (uint8_t)( + (CHUNK_DEPTH - 1) - ((-worldPos->z - 1) % CHUNK_DEPTH) + ); + } else { + localZ = (uint8_t)(worldPos->z % CHUNK_DEPTH); + } + + chunktileindex_t chunkTileIndex = (chunktileindex_t)( + (localZ * CHUNK_WIDTH * CHUNK_HEIGHT) + + (localY * CHUNK_WIDTH) + + localX + ); + assertTrue( + chunkTileIndex < CHUNK_TILE_COUNT, + "Calculated chunk tile index is out of bounds" + ); + return chunkTileIndex; +} + +chunkindex_t chunkPosToIndex(const chunkpos_t* pos) { + assertNotNull(pos, "Chunk position pointer cannot be NULL"); + + chunkindex_t chunkIndex = (chunkindex_t)( + (pos->z * MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT) + + (pos->y * MAP_CHUNK_WIDTH) + + pos->x + ); + + return chunkIndex; +} \ No newline at end of file diff --git a/src/dusk/rpg/overworld/worldpos.h b/src/dusk/rpg/overworld/worldpos.h new file mode 100644 index 00000000..8a5d74c7 --- /dev/null +++ b/src/dusk/rpg/overworld/worldpos.h @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +#define CHUNK_WIDTH 16 +#define CHUNK_HEIGHT CHUNK_WIDTH +#define CHUNK_DEPTH 8 + +#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH) + +#define MAP_CHUNK_WIDTH 3 +#define MAP_CHUNK_HEIGHT 3 +#define MAP_CHUNK_DEPTH 3 +#define MAP_CHUNK_COUNT (MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT * MAP_CHUNK_DEPTH) + +#define ENTITY_COUNT 64 + +typedef int16_t worldunit_t; +typedef int16_t chunkunit_t; +typedef int16_t chunkindex_t; +typedef uint32_t chunktileindex_t; + +typedef int32_t worldunits_t; +typedef int32_t chunkunits_t; + +typedef struct worldpos_s { + worldunit_t x, y, z; +} worldpos_t; + +typedef struct chunkpos_t { + chunkunit_t x, y, z; +} chunkpos_t; + +/** + * Compares two world positions for equality. + * + * @param a The first world position. + * @param b The second world position. + * @return true if equal, false otherwise. + */ +bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b); + +/** + * Converts a world position to a chunk position. + * + * @param worldPos The world position. + * @param out The output chunk position. + */ +void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out); + +/** + * Converts a chunk position to a world position. + * + * @param worldPos The world position. + * @param out The output chunk position. + */ +void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out); + +/** + * Converts a position in world-space to an index inside a chunk that the tile + * resides in. + * + * @param worldPos The world position. + * @return The tile index within the chunk. + */ +chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos); + +/** + * Converts a chunk position to a world position. + * + * @param worldPos The world position. + * @param out The output chunk position. + */ +chunkindex_t chunkPosToIndex(const chunkpos_t* pos); \ No newline at end of file diff --git a/src/dusk/rpg/rpg.c b/src/dusk/rpg/rpg.c new file mode 100644 index 00000000..3c04d0cf --- /dev/null +++ b/src/dusk/rpg/rpg.c @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpg.h" +#include "entity/entity.h" +#include "rpg/overworld/map.h" +#include "rpg/cutscene/cutscenesystem.h" +#include "time/time.h" +#include "rpgcamera.h" +#include "rpgtextbox.h" +#include "util/memory.h" +#include "assert/assert.h" + +errorret_t rpgInit(void) { + memoryZero(ENTITIES, sizeof(ENTITIES)); + + // Init cutscene subsystem + cutsceneSystemInit(); + + errorChain(mapInit()); + + rpgCameraInit(); + rpgTextboxInit(); + + // TEST: Create some entities. + uint8_t entIndex = entityGetAvailable(); + assertTrue(entIndex != 0xFF, "No available entity slots!."); + entity_t *ent = &ENTITIES[entIndex]; + entityInit(ent, ENTITY_TYPE_PLAYER); + // RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY; + // RPG_CAMERA.followEntity.followEntityId = ent->id; + ent->position.x = 2, ent->position.y = 2; + + // All Good! + errorOk(); +} + +errorret_t rpgUpdate(void) { + #if TIME_FIXED == 0 + if(TIME.dynamicUpdate) { + errorOk(); + } + #endif + + // TODO: Do not update if the scene is not the map scene? + mapUpdate(); + + // Update overworld ents. + entity_t *ent = &ENTITIES[0]; + do { + if(ent->type == ENTITY_TYPE_NULL) continue; + entityUpdate(ent); + } while(++ent < &ENTITIES[ENTITY_COUNT]); + + cutsceneSystemUpdate(); + errorChain(rpgCameraUpdate()); + errorOk(); +} + +void rpgDispose(void) { + mapDispose(); +} \ No newline at end of file diff --git a/src/dusk/rpg/rpg.h b/src/dusk/rpg/rpg.h new file mode 100644 index 00000000..7460dfb5 --- /dev/null +++ b/src/dusk/rpg/rpg.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" + +typedef struct { + int32_t nothing; +} rpg_t; + +/** + * Initialize the RPG subsystem. + * + * @return An error code and state. + */ +errorret_t rpgInit(void); + +/** + * Update the RPG subsystem. + * + * @return An error code. + */ +errorret_t rpgUpdate(void); + +/** + * Dispose of the RPG subsystem. + */ +void rpgDispose(void); \ No newline at end of file diff --git a/src/dusk/rpg/rpgcamera.c b/src/dusk/rpg/rpgcamera.c new file mode 100644 index 00000000..8a709cb4 --- /dev/null +++ b/src/dusk/rpg/rpgcamera.c @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpgcamera.h" +#include "util/memory.h" +#include "rpg/entity/entity.h" +#include "rpg/overworld/map.h" +#include "assert/assert.h" + +rpgcamera_t RPG_CAMERA; + +void rpgCameraInit(void) { + memoryZero(&RPG_CAMERA, sizeof(rpgcamera_t)); + +} + +errorret_t rpgCameraUpdate(void) { + glm_lookat( + (vec3){ 3, 3, 3 }, + (vec3){ 0, 0, 0 }, // center + (vec3){ 0, 1, 0 }, // up + RPG_CAMERA.eye + ); + + if(!mapIsLoaded()) errorOk(); + chunkpos_t chunkPos; + + switch(RPG_CAMERA.mode) { + case RPG_CAMERA_MODE_FREE: + worldPosToChunkPos(&RPG_CAMERA.free, &chunkPos); + break; + + case RPG_CAMERA_MODE_FOLLOW_ENTITY: { + entity_t *entity = &ENTITIES[RPG_CAMERA.followEntity.followEntityId]; + if(entity->type == ENTITY_TYPE_NULL) { + errorOk(); + } + + // Update map position to match camera. By default map wants to know the + // top left but we want to set the center, so we need to sub half map size + worldPosToChunkPos(&entity->position, &chunkPos); + break; + } + + default: + assertUnreachable("Invalid RPG camera mode"); + } + + errorChain(mapPositionSet((chunkpos_t){ + .x = chunkPos.x - (MAP_CHUNK_WIDTH / 2), + .y = chunkPos.y - (MAP_CHUNK_HEIGHT / 2), + .z = chunkPos.z - (MAP_CHUNK_DEPTH / 2) + })); + errorOk(); +} \ No newline at end of file diff --git a/src/dusk/rpg/rpgcamera.h b/src/dusk/rpg/rpgcamera.h new file mode 100644 index 00000000..82d7ca6f --- /dev/null +++ b/src/dusk/rpg/rpgcamera.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/overworld/worldpos.h" +#include "error/error.h" + +typedef enum { + RPG_CAMERA_MODE_FREE, + RPG_CAMERA_MODE_FOLLOW_ENTITY, +} rpgcameramode_t; + +typedef struct { + rpgcameramode_t mode; + + union { + worldpos_t free; + struct { + uint8_t followEntityId; + } followEntity; + }; + + mat4 eye; +} rpgcamera_t; + +extern rpgcamera_t RPG_CAMERA; + +/** + * Initializes the RPG camera. + */ +void rpgCameraInit(void); + +/** + * Updates the RPG camera. + * + * @return An error code. + */ +errorret_t rpgCameraUpdate(void); \ No newline at end of file diff --git a/src/dusk/rpg/rpgtextbox.c b/src/dusk/rpg/rpgtextbox.c new file mode 100644 index 00000000..6fba985c --- /dev/null +++ b/src/dusk/rpg/rpgtextbox.c @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpgtextbox.h" +#include "util/memory.h" +#include "util/string.h" +#include "assert/assert.h" + +rpgtextbox_t RPG_TEXTBOX; + +void rpgTextboxInit() { + memoryZero(&RPG_TEXTBOX, sizeof(rpgtextbox_t)); +} + +void rpgTextboxShow( + const rpgtextboxpos_t position, + const char_t *text +) { + RPG_TEXTBOX.position = position; + RPG_TEXTBOX.visible = true; + + stringCopy( + RPG_TEXTBOX.text, + text, + RPG_TEXTBOX_MAX_CHARS + ); +} + +void rpgTextboxHide() { + RPG_TEXTBOX.visible = false; +} + +bool_t rpgTextboxIsVisible() { + return RPG_TEXTBOX.visible; +} \ No newline at end of file diff --git a/src/dusk/rpg/rpgtextbox.h b/src/dusk/rpg/rpgtextbox.h new file mode 100644 index 00000000..02e8cd98 --- /dev/null +++ b/src/dusk/rpg/rpgtextbox.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +#define RPG_TEXTBOX_MAX_CHARS 256 + +typedef enum { + RPG_TEXTBOX_POS_TOP, + RPG_TEXTBOX_POS_BOTTOM, +} rpgtextboxpos_t; + +typedef struct { + rpgtextboxpos_t position; + bool_t visible; + char_t text[RPG_TEXTBOX_MAX_CHARS]; +} rpgtextbox_t; + +extern rpgtextbox_t RPG_TEXTBOX; + +/** + * Initializes the RPG textbox. + */ +void rpgTextboxInit(); + +/** + * Shows the RPG textbox at a specified position. + * + * @param position The position to show the textbox at. + * @param text The text to display in the textbox (copied). + */ +void rpgTextboxShow( + const rpgtextboxpos_t position, + const char_t *text +); + +/** + * Hides the RPG textbox. + */ +void rpgTextboxHide(); + +/** + * Checks if the RPG textbox is currently visible. + * + * @return true if the textbox is visible, false otherwise. + */ +bool_t rpgTextboxIsVisible(); \ No newline at end of file diff --git a/src/dusk/story/CMakeLists.txt b/src/dusk/rpg/story/CMakeLists.txt similarity index 85% rename from src/dusk/story/CMakeLists.txt rename to src/dusk/rpg/story/CMakeLists.txt index 2687e613..1b03e602 100644 --- a/src/dusk/story/CMakeLists.txt +++ b/src/dusk/rpg/story/CMakeLists.txt @@ -14,6 +14,6 @@ dusk_run_python( dusk_story_defs tools.story.csv --csv ${CMAKE_CURRENT_SOURCE_DIR}/storyflag.csv - --output ${DUSK_GENERATED_HEADERS_DIR}/story/storyflagvalue.h + --output ${DUSK_GENERATED_HEADERS_DIR}/rpg/story/storyflagvalue.h ) add_dependencies(${DUSK_LIBRARY_TARGET_NAME} dusk_story_defs) \ No newline at end of file diff --git a/src/dusk/story/storyflag.c b/src/dusk/rpg/story/storyflag.c similarity index 100% rename from src/dusk/story/storyflag.c rename to src/dusk/rpg/story/storyflag.c diff --git a/src/dusk/story/storyflag.csv b/src/dusk/rpg/story/storyflag.csv similarity index 100% rename from src/dusk/story/storyflag.csv rename to src/dusk/rpg/story/storyflag.csv diff --git a/src/dusk/story/storyflag.h b/src/dusk/rpg/story/storyflag.h similarity index 93% rename from src/dusk/story/storyflag.h rename to src/dusk/rpg/story/storyflag.h index baeec387..00fb88e0 100644 --- a/src/dusk/story/storyflag.h +++ b/src/dusk/rpg/story/storyflag.h @@ -6,7 +6,7 @@ */ #pragma once -#include "story/storyflagvalue.h" +#include "rpg/story/storyflagvalue.h" /** * Gets the value of a story flag. diff --git a/src/dusk/story/storyflagdefs.h b/src/dusk/rpg/story/storyflagdefs.h similarity index 100% rename from src/dusk/story/storyflagdefs.h rename to src/dusk/rpg/story/storyflagdefs.h diff --git a/src/dusk/scene/CMakeLists.txt b/src/dusk/scene/CMakeLists.txt index 771c68bd..9444af7b 100644 --- a/src/dusk/scene/CMakeLists.txt +++ b/src/dusk/scene/CMakeLists.txt @@ -3,13 +3,11 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -include(duskjs2c) -dusk_embed_js(${DUSK_LIBRARY_TARGET_NAME} - ${CMAKE_CURRENT_SOURCE_DIR}/scene.js -) - target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC scene.c - scenerenderpipeline.c -) \ No newline at end of file + scenetype.c +) + +# Subdirs +add_subdirectory(overworld) \ No newline at end of file diff --git a/src/dusk/entity/component/trigger/CMakeLists.txt b/src/dusk/scene/overworld/CMakeLists.txt similarity index 89% rename from src/dusk/entity/component/trigger/CMakeLists.txt rename to src/dusk/scene/overworld/CMakeLists.txt index b8c409f8..6038dc07 100644 --- a/src/dusk/entity/component/trigger/CMakeLists.txt +++ b/src/dusk/scene/overworld/CMakeLists.txt @@ -5,5 +5,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - entitytrigger.c -) + sceneoverworld.c +) \ No newline at end of file diff --git a/src/dusk/scene/overworld/sceneoverworld.c b/src/dusk/scene/overworld/sceneoverworld.c new file mode 100644 index 00000000..d73987ca --- /dev/null +++ b/src/dusk/scene/overworld/sceneoverworld.c @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "scene/scene.h" +#include "console/console.h" +#include "assert/assert.h" + +#include "display/shader/shader.h" +#include "display/screen/screen.h" +#include "display/shader/shaderunlit.h" +#include "display/spritebatch/spritebatch.h" + +#include "rpg/rpgcamera.h" + +errorret_t sceneOverworldInit(scenedata_t *sceneData) { + assertNotNull(sceneData, "Scene data cannot be null"); + + + + errorOk(); +} + +errorret_t sceneOverworldUpdate(scenedata_t *sceneData) { + assertNotNull(sceneData, "Scene data cannot be null"); + + + + errorOk(); +} + +errorret_t sceneOverworldRender(scenedata_t *sceneData) { + assertNotNull(sceneData, "Scene data cannot be null"); + + mat4 proj, model; + + glm_mat4_identity(model); + + glm_perspective( + glm_rad(45), + (float)SCREEN.width / (float)SCREEN.height, + 0.1f, + 100.0f, + proj + ); + + errorChain(shaderBind(&SHADER_UNLIT)); + errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &TEXTURE_TEST)); + errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, RPG_CAMERA.eye)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)); + + { + uint8_t spriteCount = 1; + spritebatchsprite_t sprites[spriteCount]; + + glm_vec3_copy((vec3){ -0.5f, -0.5f, 0 }, sprites[0].min); + glm_vec3_copy((vec3){ 0.5f, 0.5f, 0 }, sprites[0].max); + glm_vec2_copy((vec2){ 0, 0 }, sprites[0].uvMin); + glm_vec2_copy((vec2){ 1, 1 }, sprites[0].uvMax); + + shadermaterial_t material; + material.unlit.color = COLOR_WHITE; + material.unlit.texture = &TEXTURE_TEST; + + spriteBatchBuffer(sprites, spriteCount, &SHADER_UNLIT, material); + spriteBatchFlush(); + } + + errorOk(); +} + +errorret_t sceneOverworldDispose(scenedata_t *sceneData) { + assertNotNull(sceneData, "Scene data cannot be null"); + + + + errorOk(); +} \ No newline at end of file diff --git a/src/dusk/scene/overworld/sceneoverworld.h b/src/dusk/scene/overworld/sceneoverworld.h new file mode 100644 index 00000000..efd12f1c --- /dev/null +++ b/src/dusk/scene/overworld/sceneoverworld.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "scene/scenebase.h" + +typedef struct { + +} sceneoverworld_t; + +/** + * Initialises the overworld scene. + * + * @param sceneData The scene data used for this scene. + * @return An error if the init failed, or errorOk() if it succeeded. + */ +errorret_t sceneOverworldInit(scenedata_t *sceneData); + +/** + * Updates the overworld scene. + * + * @param sceneData The scene data used for this scene. + * @return An error if the update failed, or errorOk() if it succeeded. + */ +errorret_t sceneOverworldUpdate(scenedata_t *sceneData); + +/** + * Renders the overworld scene. + * + * @param sceneData The scene data used for this scene. + * @return An error if the render failed, or errorOk() if it succeeded. + */ +errorret_t sceneOverworldRender(scenedata_t *sceneData); + +/** + * Disposes the overworld scene. + * + * @param sceneData The scene data used for this scene. + * @return An error if the dispose failed, or errorOk() if it succeeded. + */ +errorret_t sceneOverworldDispose(scenedata_t *sceneData); \ No newline at end of file diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index 03f37def..7e530daa 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -8,50 +8,69 @@ #include "util/memory.h" #include "time/time.h" #include "display/screen/screen.h" -#include "entity/entitymanager.h" #include "display/shader/shaderunlit.h" #include "display/display.h" #include "ui/ui.h" -#include "scene/scenerenderpipeline.h" -#include "entity/component.h" #include "asset/asset.h" #include "asset/loader/assetloader.h" -#include "script/module/modulebase.h" #include "console/console.h" -#include "script/script.h" -#include "scene_js.h" scene_t SCENE; errorret_t sceneInit(void) { memoryZero(&SCENE, sizeof(scene_t)); - errorChain(scriptExecString(SCENE_JS)); errorOk(); } errorret_t sceneUpdate(void) { - #ifdef DUSK_TIME_DYNAMIC - if(TIME.dynamicUpdate) { - errorChain(scriptExecString("Scene.dynamicUpdate();")); - } else { - errorChain(scriptExecString("Scene.update();")); + // Handle scene change. + if(SCENE.next != SCENE_TYPE_NULL) { + if( + SCENE.current != SCENE_TYPE_NULL && + SCENE_TYPES[SCENE.current].dispose != NULL + ) { + errorChain(SCENE_TYPES[SCENE.current].dispose(&SCENE.data)); + } + + SCENE.current = SCENE.next; + SCENE.next = SCENE_TYPE_NULL; + + if( + SCENE.current != SCENE_TYPE_NULL && + SCENE_TYPES[SCENE.current].init != NULL + ) { + errorChain(SCENE_TYPES[SCENE.current].init(&SCENE.data)); + } + } + + #if DUSK_TIME_DYNAMIC + if(TIME.dynamicUpdate) { + errorOk(); } - #else - errorChain(scriptExecString("Scene.update();")); - errorChain(scriptExecString("Scene.dynamicUpdate();")); #endif + if( + SCENE.current != SCENE_TYPE_NULL && + SCENE_TYPES[SCENE.current].update != NULL + ) { + errorChain(SCENE_TYPES[SCENE.current].update(&SCENE.data)); + } + errorOk(); } errorret_t sceneRender(void) { + // Scene rendering + if( + SCENE.current != SCENE_TYPE_NULL && + SCENE_TYPES[SCENE.current].render != NULL + ) { + errorChain(SCENE_TYPES[SCENE.current].render(&SCENE.data)); + } + + // UI Rendering mat4 proj, view, ident; glm_mat4_identity(ident); - - errorChain(componentRenderAll()); - errorChain(sceneRenderPipeline(entityCameraGetCurrent())); - - // UI Rendering errorChain(shaderBind(&SHADER_UNLIT)); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, ident)); @@ -79,7 +98,18 @@ errorret_t sceneRender(void) { errorOk(); } +void sceneSet(const scenetype_t type) { + assertTrue(type < SCENE_TYPE_COUNT, "Invalid scene type."); + SCENE.next = type; +} + errorret_t sceneDispose(void) { - errorChain(scriptExecString("Scene.dispose();")); + if( + SCENE.current != SCENE_TYPE_NULL && + SCENE_TYPES[SCENE.current].dispose != NULL + ) { + errorChain(SCENE_TYPES[SCENE.current].dispose(&SCENE.data)); + } + errorOk(); } diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h index e7ecfe39..b3cd8168 100644 --- a/src/dusk/scene/scene.h +++ b/src/dusk/scene/scene.h @@ -6,10 +6,12 @@ */ #pragma once -#include "error/error.h" +#include "scenetype.h" typedef struct { - void *nothing; + scenetype_t current; + scenetype_t next; + scenedata_t data; } scene_t; extern scene_t SCENE; @@ -36,6 +38,13 @@ errorret_t sceneUpdate(void); */ errorret_t sceneRender(void); +/** + * Requests the next frame to change to this scene. + * + * @param type The scene type to change to. + */ +void sceneSet(const scenetype_t type); + /** * Disposes the active scene immediately. * diff --git a/src/dusk/scene/scene.js b/src/dusk/scene/scene.js deleted file mode 100644 index c133b448..00000000 --- a/src/dusk/scene/scene.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2026 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -var Scene = { -}; - -Scene.update = () => { - if(!Scene.current || !Scene.current.update) return; - Scene.current.update(); -} - -Scene.dynamicUpdate = () => { - if(!Scene.current || !Scene.current.dynamicUpdate) return; - Scene.current.dynamicUpdate(); -} - -Scene.set = (newScene) => { - // Current scene active? - if(Scene.current && Scene.current.dispose) { - Scene.current.dispose(); - } - - // Set new scene - Scene.current = newScene; - if(!newScene) return; - - // Init - if(newScene.init) { - newScene.init(); - } -}; - -Scene.dispose = () => { - if(Scene.current && Scene.current.dispose) { - Scene.current.dispose(); - } - Scene.current = null; -} \ No newline at end of file diff --git a/src/dusk/script/scriptmodule.h b/src/dusk/scene/scenebase.h similarity index 65% rename from src/dusk/script/scriptmodule.h rename to src/dusk/scene/scenebase.h index fef0f58f..7846a9cc 100644 --- a/src/dusk/script/scriptmodule.h +++ b/src/dusk/scene/scenebase.h @@ -6,8 +6,6 @@ */ #pragma once -#include "scriptproto.h" +#include "error/error.h" -typedef struct { - jerry_value_t exports; -} scriptmodule_t; \ No newline at end of file +typedef union scenedata_u scenedata_t; \ No newline at end of file diff --git a/src/dusk/scene/scenerenderpipeline.c b/src/dusk/scene/scenerenderpipeline.c deleted file mode 100644 index 0ed2c8e8..00000000 --- a/src/dusk/scene/scenerenderpipeline.c +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "scenerenderpipeline.h" -#include "entity/entitymanager.h" -#include "entity/component/display/entityposition.h" -#include "entity/component/display/entitycamera.h" -#include "display/shader/shaderlist.h" -#include "display/shader/shaderunlit.h" -#include "display/displaystate.h" -#include "assert/assert.h" - -int8_t sceneRenderPipelineGetPriority(const entityrenderable_t *r) { - assertNotNull(r, "Renderable cannot be null"); - if(r->priority != 0) return r->priority; - switch(r->type) { - case ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL: - return (r->data.material.state.flags & DISPLAY_STATE_FLAG_BLEND) - ? (int8_t)10 : (int8_t)-10; - case ENTITY_RENDERABLE_TYPE_SPRITEBATCH: - return (int8_t)100; - case ENTITY_RENDERABLE_TYPE_CUSTOM: - default: - return (int8_t)0; - } -} - -int_t sceneRenderPipelineCompare(const void *a, const void *b) { - assertNotNull(a, "Entry a cannot be null"); - assertNotNull(b, "Entry b cannot be null"); - const scenerenderpipelineentry_t *ea = (const scenerenderpipelineentry_t *)a; - const scenerenderpipelineentry_t *eb = (const scenerenderpipelineentry_t *)b; - return (int_t)ea->effectivePriority - (int_t)eb->effectivePriority; -} - -shader_t *sceneRenderPipelineGetShader(const entityrenderable_t *r) { - assertNotNull(r, "Renderable cannot be null"); - switch(r->type) { - case ENTITY_RENDERABLE_TYPE_SPRITEBATCH: - return SHADER_LIST_DEFS[SHADER_LIST_SHADER_UNLIT].shader; - case ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL: - return SHADER_LIST_DEFS[r->data.material.shaderType].shader; - case ENTITY_RENDERABLE_TYPE_CUSTOM: - default: - return SHADER_LIST_DEFS[SHADER_LIST_SHADER_UNLIT].shader; - } -} - -errorret_t sceneRenderPipeline(const entityid_t cameraId) { - mat4 proj, view, model, ident; - glm_mat4_identity(ident); - - entityid_t entities[ENTITY_COUNT_MAX]; - componentid_t components[ENTITY_COUNT_MAX]; - entityid_t entCount = componentGetEntitiesWithComponent( - COMPONENT_TYPE_RENDERABLE, entities, components - ); - - if(cameraId == ENTITY_ID_INVALID || entCount == 0) errorOk(); - - scenerenderpipelineentry_t pipeline[ENTITY_COUNT_MAX]; - for(entityid_t i = 0; i < entCount; i++) { - entityrenderable_t *r = componentGetData( - entities[i], components[i], COMPONENT_TYPE_RENDERABLE - ); - pipeline[i].entityId = entities[i]; - pipeline[i].componentId = components[i]; - pipeline[i].effectivePriority = sceneRenderPipelineGetPriority(r); - } - sort( - pipeline, - (size_t)entCount, - sizeof(scenerenderpipelineentry_t), - sceneRenderPipelineCompare - ); - - componentid_t camPos = entityGetComponent(cameraId, COMPONENT_TYPE_POSITION); - componentid_t camCam = entityGetComponent(cameraId, COMPONENT_TYPE_CAMERA); - assertTrue( - camCam != COMPONENT_ID_INVALID, - "Current camera does not have a camera component?" - ); - - if(camPos == COMPONENT_ID_INVALID) { - glm_mat4_copy(ident, view); - } else { - entityPositionGetTransform(cameraId, camPos, view); - } - entityCameraGetProjection(cameraId, camCam, proj); - - for( - shaderlistshader_t si = SHADER_LIST_SHADER_NULL + 1; - si < SHADER_LIST_SHADER_COUNT; - si++ - ) { - shader_t *s = SHADER_LIST_DEFS[si].shader; - assertNotNull(s, "Shader in list cannot be null"); - errorChain(shaderBind(s)); - errorChain(shaderSetMatrix(s, SHADER_UNLIT_VIEW, view)); - errorChain(shaderSetMatrix(s, SHADER_UNLIT_PROJECTION, proj)); - } - - for(entityid_t i = 0; i < entCount; i++) { - entityid_t eid = pipeline[i].entityId; - componentid_t cid = pipeline[i].componentId; - - entityrenderable_t *r = componentGetData( - eid, cid, COMPONENT_TYPE_RENDERABLE - ); - shader_t *s = sceneRenderPipelineGetShader(r); - - componentid_t posComp = entityGetComponent(eid, COMPONENT_TYPE_POSITION); - if(posComp == COMPONENT_ID_INVALID) { - errorChain(shaderBind(s)); - errorChain(shaderSetMatrix(s, SHADER_UNLIT_MODEL, ident)); - } else { - entityPositionGetTransform(eid, posComp, model); - errorChain(shaderBind(s)); - errorChain(shaderSetMatrix(s, SHADER_UNLIT_MODEL, model)); - } - - errorChain(entityRenderableDraw(eid, cid)); - } - - errorChain(displaySetState((displaystate_t){ .flags = 0 })); - errorOk(); -} diff --git a/src/dusk/scene/scenerenderpipeline.h b/src/dusk/scene/scenerenderpipeline.h deleted file mode 100644 index 637ee591..00000000 --- a/src/dusk/scene/scenerenderpipeline.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * 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 "entity/component/display/entityrenderable.h" -#include "display/shader/shader.h" -#include "util/sort.h" - -typedef struct { - entityid_t entityId; - componentid_t componentId; - int8_t effectivePriority; -} scenerenderpipelineentry_t; - -/** - * Returns the effective render priority for a renderable. When the renderable's - * explicit priority is non-zero that value is returned directly; otherwise an - * automatic value is derived from the renderable type and display state flags. - * - * @param r The renderable component data. - * @return Effective priority: lower renders first, higher renders last. - */ -int8_t sceneRenderPipelineGetPriority(const entityrenderable_t *r); - -/** - * sortcompare_t comparator for scenerenderpipelineentry_t. Compares by - * effectivePriority ascending so lower-priority entries sort first. - * - * @param a Pointer to the first scenerenderpipelineentry_t. - * @param b Pointer to the second scenerenderpipelineentry_t. - * @return Negative, zero, or positive. - */ -int_t sceneRenderPipelineCompare(const void *a, const void *b); - -/** - * Returns the shader that will be used to render the given renderable. - * SPRITEBATCH and CUSTOM default to SHADER_UNLIT; SHADER_MATERIAL uses the - * shader indexed by the material's shaderType field in SHADER_LIST_DEFS. - * - * @param r The renderable component data. - * @return Pointer to the shader, never NULL. - */ -shader_t *sceneRenderPipelineGetShader(const entityrenderable_t *r); - -/** - * Renders all entities with renderable components in priority order. - * Lower priority renders first (behind); higher renders last (on top). - * When priority is 0 the effective value is derived from the renderable's type - * and flags; otherwise the explicit priority is used directly. - * - * @param cameraId The entity ID of the active camera, or ENTITY_ID_INVALID. - * @return Error state. - */ -errorret_t sceneRenderPipeline(const entityid_t cameraId); diff --git a/src/dusk/scene/scenetype.c b/src/dusk/scene/scenetype.c new file mode 100644 index 00000000..e2e76d7e --- /dev/null +++ b/src/dusk/scene/scenetype.c @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "scenetype.h" + +scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = { + [SCENE_TYPE_NULL] = { 0 }, + + [SCENE_TYPE_OVERWORLD] = { + .init = sceneOverworldInit, + .update = sceneOverworldUpdate, + .render = sceneOverworldRender, + .dispose = sceneOverworldDispose + }, +}; + diff --git a/src/dusk/scene/scenetype.h b/src/dusk/scene/scenetype.h new file mode 100644 index 00000000..9e981678 --- /dev/null +++ b/src/dusk/scene/scenetype.h @@ -0,0 +1,33 @@ +/** + * 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 "scene/overworld/sceneoverworld.h" + +typedef union scenedata_u { + sceneoverworld_t overworld; +} scenedata_t; + +typedef errorret_t (*scenecallback_t)(scenedata_t *); + +typedef struct { + scenecallback_t init; + scenecallback_t update; + scenecallback_t render; + scenecallback_t dispose; +} scenecallbacks_t; + +typedef enum { + SCENE_TYPE_NULL, + + SCENE_TYPE_OVERWORLD, + + SCENE_TYPE_COUNT +} scenetype_t; + +extern scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT]; \ No newline at end of file diff --git a/src/dusk/script/CMakeLists.txt b/src/dusk/script/CMakeLists.txt deleted file mode 100644 index 9f110960..00000000 --- a/src/dusk/script/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - script.c - scriptpromisepend.c - scriptproto.c -) - -# Subdirectories -add_subdirectory(module) \ No newline at end of file diff --git a/src/dusk/script/module/CMakeLists.txt b/src/dusk/script/module/CMakeLists.txt deleted file mode 100644 index 746538c2..00000000 --- a/src/dusk/script/module/CMakeLists.txt +++ /dev/null @@ -1,30 +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 - modulebase.c - modulelist.c -) - -# Subdirs -add_subdirectory(animation) -add_subdirectory(asset) -add_subdirectory(console) -add_subdirectory(display) -add_subdirectory(engine) -add_subdirectory(entity) -add_subdirectory(event) -add_subdirectory(input) -add_subdirectory(item) -add_subdirectory(locale) -add_subdirectory(math) -add_subdirectory(overworld) -add_subdirectory(require) -add_subdirectory(save) -add_subdirectory(scene) -add_subdirectory(story) -add_subdirectory(system) -add_subdirectory(ui) \ No newline at end of file diff --git a/src/dusk/script/module/animation/CMakeLists.txt b/src/dusk/script/module/animation/CMakeLists.txt deleted file mode 100644 index 3483063d..00000000 --- a/src/dusk/script/module/animation/CMakeLists.txt +++ /dev/null @@ -1,10 +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 - moduleanimation.c - moduleeasing.c -) diff --git a/src/dusk/script/module/animation/moduleanimation.c b/src/dusk/script/module/animation/moduleanimation.c deleted file mode 100644 index dcab0c14..00000000 --- a/src/dusk/script/module/animation/moduleanimation.c +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleanimation.h" -#include "animation/animation.h" - -typedef struct { - keyframe_t keyframes[MODULE_ANIMATION_KEYFRAME_MAX]; - animation_t anim; -} moduleanimationdata_t; - -scriptproto_t MODULE_ANIMATION_PROTO; - -moduleBaseFunction(moduleAnimationCtor) { - if(argc < 1 || !jerry_value_is_array(args[0])) { - return moduleBaseThrow( - "Animation: expected array of keyframe objects" - ); - } - uint32_t len = jerry_array_length(args[0]); - if(len == 0) { - return moduleBaseThrow( - "Animation: keyframe array must not be empty" - ); - } - const uint16_t count = len > (uint32_t)MODULE_ANIMATION_KEYFRAME_MAX - ? (uint16_t)MODULE_ANIMATION_KEYFRAME_MAX - : (uint16_t)len; - - moduleanimationdata_t *d = (moduleanimationdata_t *)memoryAllocate( - sizeof(moduleanimationdata_t) - ); - - for(uint16_t i = 0; i < count; i++) { - jerry_value_t elem = jerry_object_get_index(args[0], (uint32_t)i); - jerry_value_t jtm = moduleBaseGetProp(elem, "time"); - jerry_value_t jvl = moduleBaseGetProp(elem, "value"); - jerry_value_t jea = moduleBaseGetProp(elem, "easing"); - - d->keyframes[i].time = moduleBaseValueFloat(jtm); - d->keyframes[i].value = moduleBaseValueFloat(jvl); - d->keyframes[i].easing = jerry_value_is_number(jea) - ? (easingtype_t)moduleBaseValueInt(jea) - : EASING_LINEAR; - - jerry_value_free(jea); - jerry_value_free(jvl); - jerry_value_free(jtm); - jerry_value_free(elem); - } - - animationInit(&d->anim, d->keyframes, count); - - jerry_object_set_native_ptr( - callInfo->this_value, &MODULE_ANIMATION_PROTO.info, d - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleAnimationGetValue) { - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - moduleanimationdata_t *d = (moduleanimationdata_t *)scriptProtoGetValue( - &MODULE_ANIMATION_PROTO, callInfo->this_value - ); - if(!d) return jerry_undefined(); - return jerry_number( - (double)animationGetValue(&d->anim, moduleBaseArgFloat(0)) - ); -} - -void moduleAnimationInit(void) { - scriptProtoInit( - &MODULE_ANIMATION_PROTO, "Animation", - sizeof(moduleanimationdata_t), moduleAnimationCtor - ); - scriptProtoDefineFunc( - &MODULE_ANIMATION_PROTO, "getValue", moduleAnimationGetValue - ); -} - -void moduleAnimationDispose(void) { - scriptProtoDispose(&MODULE_ANIMATION_PROTO); -} diff --git a/src/dusk/script/module/animation/moduleanimation.h b/src/dusk/script/module/animation/moduleanimation.h deleted file mode 100644 index cd2b3def..00000000 --- a/src/dusk/script/module/animation/moduleanimation.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "util/memory.h" - -/** Maximum keyframes per Animation instance. */ -#define MODULE_ANIMATION_KEYFRAME_MAX 64 - -extern scriptproto_t MODULE_ANIMATION_PROTO; - -/** - * new Animation(keyframes) - creates an Animation from a JS array of - * `{time, value, easing?}` descriptor objects. - * - * @param args[0] Array of keyframe descriptors. - */ -moduleBaseFunction(moduleAnimationCtor); - -/** - * animation.getValue(time) - interpolates the animation at `time`. - * - * @param args[0] Time in seconds (number). - * @return Interpolated float value. - */ -moduleBaseFunction(moduleAnimationGetValue); - -/** - * Initializes the Animation module and registers the Animation constructor. - */ -void moduleAnimationInit(void); - -/** - * Disposes the Animation module. - */ -void moduleAnimationDispose(void); diff --git a/src/dusk/script/module/animation/moduleeasing.c b/src/dusk/script/module/animation/moduleeasing.c deleted file mode 100644 index 65bdcaea..00000000 --- a/src/dusk/script/module/animation/moduleeasing.c +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleeasing.h" -#include "animation/easing.h" - -scriptproto_t MODULE_EASING_PROTO; - -moduleBaseFunction(moduleEasingApply) { - moduleBaseRequireArgs(2); - moduleBaseRequireNumber(0); - moduleBaseRequireNumber(1); - const int32_t type = moduleBaseArgInt(0); - if(type < 0 || type >= (int32_t)EASING_COUNT) { - return moduleBaseThrow("Easing.apply: invalid easing type"); - } - const float_t t = moduleBaseArgFloat(1); - return jerry_number((double)easingApply((easingtype_t)type, t)); -} - -void moduleEasingInit(void) { - scriptProtoInit(&MODULE_EASING_PROTO, "Easing", 0, NULL); - scriptProtoDefineStaticFunc( - &MODULE_EASING_PROTO, "apply", moduleEasingApply - ); - - moduleBaseSetInt("EASING_LINEAR", EASING_LINEAR); - moduleBaseSetInt("EASING_IN_SINE", EASING_IN_SINE); - moduleBaseSetInt("EASING_OUT_SINE", EASING_OUT_SINE); - moduleBaseSetInt("EASING_IN_OUT_SINE", EASING_IN_OUT_SINE); - moduleBaseSetInt("EASING_IN_QUAD", EASING_IN_QUAD); - moduleBaseSetInt("EASING_OUT_QUAD", EASING_OUT_QUAD); - moduleBaseSetInt("EASING_IN_OUT_QUAD", EASING_IN_OUT_QUAD); - moduleBaseSetInt("EASING_IN_CUBIC", EASING_IN_CUBIC); - moduleBaseSetInt("EASING_OUT_CUBIC", EASING_OUT_CUBIC); - moduleBaseSetInt("EASING_IN_OUT_CUBIC", EASING_IN_OUT_CUBIC); - moduleBaseSetInt("EASING_IN_QUART", EASING_IN_QUART); - moduleBaseSetInt("EASING_OUT_QUART", EASING_OUT_QUART); - moduleBaseSetInt("EASING_IN_OUT_QUART", EASING_IN_OUT_QUART); - moduleBaseSetInt("EASING_IN_BACK", EASING_IN_BACK); - moduleBaseSetInt("EASING_OUT_BACK", EASING_OUT_BACK); - moduleBaseSetInt("EASING_IN_OUT_BACK", EASING_IN_OUT_BACK); -} - -void moduleEasingDispose(void) { - scriptProtoDispose(&MODULE_EASING_PROTO); -} diff --git a/src/dusk/script/module/animation/moduleeasing.h b/src/dusk/script/module/animation/moduleeasing.h deleted file mode 100644 index fe9f48a2..00000000 --- a/src/dusk/script/module/animation/moduleeasing.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" - -extern scriptproto_t MODULE_EASING_PROTO; - -/** - * Easing.apply(type, t) - applies easing function `type` to normalized - * time `t` and returns the eased value. - * - * @param args[0] Easing type constant (EASING_*). - * @param args[1] Normalized input time in [0, 1]. - * @return Eased value as a number. - */ -moduleBaseFunction(moduleEasingApply); - -/** - * Initializes the Easing module and injects all EASING_* constants. - */ -void moduleEasingInit(void); - -/** - * Disposes the Easing module. - */ -void moduleEasingDispose(void); diff --git a/src/dusk/script/module/asset/CMakeLists.txt b/src/dusk/script/module/asset/CMakeLists.txt deleted file mode 100644 index 801d99ea..00000000 --- a/src/dusk/script/module/asset/CMakeLists.txt +++ /dev/null @@ -1,11 +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 - moduleassetentry.c - moduleassetbatch.c - moduleasset.c -) diff --git a/src/dusk/script/module/asset/moduleasset.c b/src/dusk/script/module/asset/moduleasset.c deleted file mode 100644 index 58fe8f62..00000000 --- a/src/dusk/script/module/asset/moduleasset.c +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleasset.h" - -scriptproto_t MODULE_ASSET_PROTO; - -moduleBaseFunction(moduleAssetExists) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - char_t buf[256]; - moduleBaseToString(args[0], buf, sizeof(buf)); - return jerry_boolean(assetFileExists(buf)); -} - -moduleBaseFunction(moduleAssetLock) { - moduleBaseRequireArgs(2); - moduleBaseRequireString(0); - moduleBaseRequireNumber(1); - - char_t buf[256]; - moduleBaseToString(args[0], buf, sizeof(buf)); - assetloadertype_t type = (assetloadertype_t)moduleBaseArgInt(1); - - assetloaderinput_t input; - assetloaderinput_t *inputPtr = NULL; - - if(argc >= 3 && jerry_value_is_number(args[2])) { - int32_t inputVal = moduleBaseArgInt(2); - switch(type) { - case ASSET_LOADER_TYPE_TEXTURE: - input.texture = (textureformat_t)inputVal; - inputPtr = &input; - break; - case ASSET_LOADER_TYPE_MESH: - input.mesh = (assetmeshinputaxis_t)inputVal; - inputPtr = &input; - break; - default: - break; - } - } - - assetentry_t *entry = assetLock(buf, type, inputPtr); - if(!entry) return moduleBaseThrow("Asset.lock: failed to lock asset"); - jsassetentry_t e = { .entry = entry }; - return scriptProtoCreateValue(&MODULE_ASSET_ENTRY_PROTO, &e); -} - -moduleBaseFunction(moduleAssetRequireLoaded) { - moduleBaseRequireArgs(1); - jsassetentry_t *e = (jsassetentry_t *)scriptProtoGetValue( - &MODULE_ASSET_ENTRY_PROTO, args[0] - ); - if(!e || !e->entry) { - return moduleBaseThrow("Asset.requireLoaded: expected AssetEntry"); - } - errorret_t err = assetRequireLoaded(e->entry); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_value_copy(args[0]); -} - -moduleBaseFunction(moduleAssetUnlock) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - char_t buf[256]; - moduleBaseToString(args[0], buf, sizeof(buf)); - assetUnlock(buf); - return jerry_undefined(); -} - -void moduleAssetInit(void) { - moduleAssetEntryInit(); - moduleAssetBatchInit(); - scriptProtoInit(&MODULE_ASSET_PROTO, "Asset", sizeof(uint8_t), NULL); - scriptProtoDefineStaticFunc( - &MODULE_ASSET_PROTO, "exists", moduleAssetExists - ); - scriptProtoDefineStaticFunc( - &MODULE_ASSET_PROTO, "lock", moduleAssetLock - ); - scriptProtoDefineStaticFunc( - &MODULE_ASSET_PROTO, "unlock", moduleAssetUnlock - ); - scriptProtoDefineStaticFunc( - &MODULE_ASSET_PROTO, "requireLoaded", moduleAssetRequireLoaded - ); - - jerry_value_t global = MODULE_ASSET_PROTO.prototype; - - struct { const char_t *name; int val; } types[] = { - { "TYPE_MESH", ASSET_LOADER_TYPE_MESH }, - { "TYPE_TEXTURE", ASSET_LOADER_TYPE_TEXTURE }, - { "TYPE_TILESET", ASSET_LOADER_TYPE_TILESET }, - { "TYPE_LOCALE", ASSET_LOADER_TYPE_LOCALE }, - { "TYPE_JSON", ASSET_LOADER_TYPE_JSON }, - { "TYPE_SCRIPT", ASSET_LOADER_TYPE_SCRIPT }, - }; - for(int i = 0; i < 6; i++) { - jerry_value_t k = jerry_string_sz(types[i].name); - jerry_value_t v = jerry_number((double)types[i].val); - jerry_object_set(global, k, v); - jerry_value_free(v); - jerry_value_free(k); - } - - struct { const char_t *name; int val; } axes[] = { - { "MESH_AXIS_Y_UP", MESH_INPUT_AXIS_Y_UP }, - { "MESH_AXIS_Z_UP", MESH_INPUT_AXIS_Z_UP }, - { "MESH_AXIS_X_UP", MESH_INPUT_AXIS_X_UP }, - { "MESH_AXIS_Y_DOWN", MESH_INPUT_AXIS_Y_DOWN }, - { "MESH_AXIS_Z_DOWN", MESH_INPUT_AXIS_Z_DOWN }, - { "MESH_AXIS_X_DOWN", MESH_INPUT_AXIS_X_DOWN }, - }; - for(int i = 0; i < 6; i++) { - jerry_value_t k = jerry_string_sz(axes[i].name); - jerry_value_t v = jerry_number((double)axes[i].val); - jerry_object_set(global, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleAssetDispose(void) { - scriptProtoDispose(&MODULE_ASSET_PROTO); - moduleAssetBatchDispose(); - moduleAssetEntryDispose(); -} diff --git a/src/dusk/script/module/asset/moduleasset.h b/src/dusk/script/module/asset/moduleasset.h deleted file mode 100644 index cef603b2..00000000 --- a/src/dusk/script/module/asset/moduleasset.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/display/moduletexture.h" -#include "script/module/asset/moduleassetentry.h" -#include "script/module/asset/moduleassetbatch.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" - -extern scriptproto_t MODULE_ASSET_PROTO; - -/** - * Asset.exists(path) - returns true if the path exists in the asset archive. - * @param args[0] Archive-relative path string. - */ -moduleBaseFunction(moduleAssetExists); - -/** - * Asset.lock(path, type, input?) - locks an asset entry and returns an - * AssetEntry. The entry begins loading in the background. - * - * @param args[0] Path string. - * @param args[1] Loader type constant (Asset.TYPE_*). - * @param args[2] Optional loader input (Texture.FORMAT_* or Asset.MESH_AXIS_*). - */ -moduleBaseFunction(moduleAssetLock); - -/** - * Asset.requireLoaded(entry) - blocks until the entry is fully loaded. - * @param args[0] An AssetEntry object. - * @return The same AssetEntry for chaining. - * @throws If the load fails. - */ -moduleBaseFunction(moduleAssetRequireLoaded); - -/** - * Asset.unlock(path) - releases the asset lock for the given path. - * Prefer entry.unlock() on the AssetEntry object directly. - * @param args[0] Path string originally passed to Asset.lock(). - */ -moduleBaseFunction(moduleAssetUnlock); - -/** - * Initializes the Asset module. Also calls moduleAssetEntryInit() and - * moduleAssetBatchInit(), then registers the global Asset object with - * exists/lock/unlock/requireLoaded and all TYPE_* / MESH_AXIS_* constants. - */ -void moduleAssetInit(void); - -/** - * Disposes the Asset module and calls moduleAssetBatchDispose() and - * moduleAssetEntryDispose(). - */ -void moduleAssetDispose(void); diff --git a/src/dusk/script/module/asset/moduleassetbatch.c b/src/dusk/script/module/asset/moduleassetbatch.c deleted file mode 100644 index ea145c69..00000000 --- a/src/dusk/script/module/asset/moduleassetbatch.c +++ /dev/null @@ -1,388 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleassetbatch.h" -#include "util/string.h" - -#define ASSET_BATCH_PEND_MAX 8 - -scriptproto_t MODULE_ASSET_BATCH_PROTO; - -static scriptpromisepend_t ASSET_BATCH_PEND[ASSET_BATCH_PEND_MAX]; -static uint32_t ASSET_BATCH_PEND_COUNT = 0; - -void assetBatchOnLoadedFire(void *params, void *user) { - assetbatch_t *batch = (assetbatch_t *)user; - jerry_value_t undef = jerry_undefined(); - uint32_t n = scriptPromisePendResolve( - ASSET_BATCH_PEND, &ASSET_BATCH_PEND_COUNT, batch, undef - ); - jerry_value_free(undef); - if(!n) return; - eventUnsubscribe(&batch->onLoaded, assetBatchOnLoadedFire); - eventUnsubscribe(&batch->onError, assetBatchOnErrorFire); -} - -void assetBatchOnErrorFire(void *params, void *user) { - assetbatch_t *batch = (assetbatch_t *)user; - jerry_value_t err = jerry_string_sz("Asset batch failed to load"); - uint32_t n = scriptPromisePendReject( - ASSET_BATCH_PEND, &ASSET_BATCH_PEND_COUNT, batch, err - ); - jerry_value_free(err); - if(!n) return; - eventUnsubscribe(&batch->onLoaded, assetBatchOnLoadedFire); - eventUnsubscribe(&batch->onError, assetBatchOnErrorFire); -} - -void moduleAssetBatchFree(void *ptr, jerry_object_native_info_t *info) { - jsassetbatch_t *b = (jsassetbatch_t *)ptr; - if(b && b->batch) { - assetBatchDispose(b->batch); - memoryFree(b->batch); - } - memoryFree(ptr); -} - -jsassetbatch_t *moduleAssetBatchSelf(const jerry_call_info_t *callInfo) { - return (jsassetbatch_t *)scriptProtoGetValue( - &MODULE_ASSET_BATCH_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(moduleAssetBatchCtor) { - if(argc < 1 || !jerry_value_is_array(args[0])) { - return moduleBaseThrow("AssetBatch: expected an array of descriptors"); - } - - uint32_t count = jerry_array_length(args[0]); - if(count == 0 || count > (uint32_t)ASSET_BATCH_COUNT_MAX) { - return moduleBaseThrow("AssetBatch: descriptor count out of range"); - } - - assetbatchdesc_t descs[ASSET_BATCH_COUNT_MAX]; - char_t paths[ASSET_BATCH_COUNT_MAX][ASSET_FILE_NAME_MAX]; - - for(uint32_t i = 0; i < count; i++) { - jerry_value_t desc = jerry_object_get_index(args[0], i); - if(!jerry_value_is_object(desc)) { - jerry_value_free(desc); - return moduleBaseThrow( - "AssetBatch: each descriptor must be an object" - ); - } - - jerry_value_t pathProp = moduleBaseGetProp(desc, "path"); - if(!jerry_value_is_string(pathProp)) { - jerry_value_free(pathProp); - jerry_value_free(desc); - return moduleBaseThrow( - "AssetBatch: descriptor.path must be a string" - ); - } - jerry_size_t pathLen = jerry_string_to_buffer( - pathProp, JERRY_ENCODING_UTF8, - (jerry_char_t *)paths[i], ASSET_FILE_NAME_MAX - 1 - ); - paths[i][pathLen] = '\0'; - jerry_value_free(pathProp); - descs[i].path = paths[i]; - - jerry_value_t typeProp = moduleBaseGetProp(desc, "type"); - if(!jerry_value_is_number(typeProp)) { - jerry_value_free(typeProp); - jerry_value_free(desc); - return moduleBaseThrow( - "AssetBatch: descriptor.type must be a number" - ); - } - descs[i].type = (assetloadertype_t)moduleBaseValueInt(typeProp); - jerry_value_free(typeProp); - - memoryZero(&descs[i].input, sizeof(assetloaderinput_t)); - jerry_value_t inputProp = moduleBaseGetProp(desc, "format"); - if(jerry_value_is_undefined(inputProp)) { - jerry_value_free(inputProp); - inputProp = moduleBaseGetProp(desc, "input"); - } - if(jerry_value_is_undefined(inputProp)) { - jerry_value_free(inputProp); - inputProp = moduleBaseGetProp(desc, "axis"); - } - if(jerry_value_is_number(inputProp)) { - int32_t v = moduleBaseValueInt(inputProp); - switch(descs[i].type) { - case ASSET_LOADER_TYPE_TEXTURE: - descs[i].input.texture = (textureformat_t)v; - break; - case ASSET_LOADER_TYPE_MESH: - descs[i].input.mesh = (assetmeshinputaxis_t)v; - break; - default: - break; - } - } - jerry_value_free(inputProp); - jerry_value_free(desc); - } - - assetbatch_t *batch = (assetbatch_t *)memoryAllocate(sizeof(assetbatch_t)); - assetBatchInit(batch, (uint16_t)count, descs); - - jsassetbatch_t init = { .batch = batch }; - return scriptProtoCreateValue(&MODULE_ASSET_BATCH_PROTO, &init); -} - -moduleBaseFunction(moduleAssetBatchGetCount) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_number(0.0); - return jerry_number((double)b->batch->count); -} - -moduleBaseFunction(moduleAssetBatchGetIsLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_boolean(false); - return jerry_boolean(assetBatchIsLoaded(b->batch)); -} - -moduleBaseFunction(moduleAssetBatchGetHasError) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_boolean(false); - return jerry_boolean(assetBatchHasError(b->batch)); -} - -moduleBaseFunction(moduleAssetBatchRequireLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) { - return moduleBaseThrow( - "AssetBatch.requireLoaded: batch already disposed" - ); - } - errorret_t err = assetBatchRequireLoaded(b->batch); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_value_copy(callInfo->this_value); -} - -moduleBaseFunction(moduleAssetBatchLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) { - return moduleBaseThrow("AssetBatch.loaded: batch disposed"); - } - - jerry_value_t promise = jerry_promise(); - - if(assetBatchIsLoaded(b->batch)) { - jerry_value_t undef = jerry_undefined(); - jerry_value_t r = jerry_promise_resolve(promise, undef); - jerry_value_free(undef); - jerry_value_free(r); - return promise; - } - - if(assetBatchHasError(b->batch)) { - jerry_value_t err = jerry_string_sz("Asset batch failed to load"); - jerry_value_t r = jerry_promise_reject(promise, err); - jerry_value_free(err); - jerry_value_free(r); - return promise; - } - - if(ASSET_BATCH_PEND_COUNT >= ASSET_BATCH_PEND_MAX) { - jerry_value_free(promise); - return moduleBaseThrow("AssetBatch.loaded: too many pending"); - } - - if(!scriptPromisePendHas( - ASSET_BATCH_PEND, ASSET_BATCH_PEND_COUNT, b->batch - )) { - eventSubscribe( - &b->batch->onLoaded, assetBatchOnLoadedFire, b->batch - ); - eventSubscribe( - &b->batch->onError, assetBatchOnErrorFire, b->batch - ); - } - - scriptPromisePendAdd( - ASSET_BATCH_PEND, &ASSET_BATCH_PEND_COUNT, - ASSET_BATCH_PEND_MAX, b->batch, promise - ); - return promise; -} - -moduleBaseFunction(moduleAssetBatchLock) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - assetBatchLock(b->batch); - return jerry_value_copy(callInfo->this_value); -} - -moduleBaseFunction(moduleAssetBatchUnlock) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - assetBatchUnlock(b->batch); - assetBatchDispose(b->batch); - memoryFree(b->batch); - b->batch = NULL; - return jerry_undefined(); -} - -moduleBaseFunction(moduleAssetBatchEntry) { - moduleBaseRequireArgs(1); - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - uint32_t idx = (uint32_t)moduleBaseArgInt(0); - if(idx >= (uint32_t)b->batch->count) return jerry_undefined(); - assetentry_t *entry = b->batch->entries[idx]; - if(!entry) return jerry_undefined(); - assetEntryLock(entry); - jsassetentry_t e = { .entry = entry }; - return scriptProtoCreateValue(&MODULE_ASSET_ENTRY_PROTO, &e); -} - -moduleBaseFunction(moduleAssetBatchGetAssetByPath) { - moduleBaseRequireArgs(1); - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - if(!jerry_value_is_string(args[0])) return jerry_undefined(); - - char_t path[ASSET_FILE_NAME_MAX]; - jerry_size_t pathLen = jerry_string_to_buffer( - args[0], JERRY_ENCODING_UTF8, - (jerry_char_t *)path, ASSET_FILE_NAME_MAX - 1 - ); - path[pathLen] = '\0'; - - for(uint16_t i = 0; i < b->batch->count; i++) { - assetentry_t *entry = b->batch->entries[i]; - if(!entry) continue; - if(!stringEquals(entry->name, path)) continue; - assetEntryLock(entry); - jsassetentry_t e = { .entry = entry }; - return scriptProtoCreateValue(&MODULE_ASSET_ENTRY_PROTO, &e); - } - return jerry_undefined(); -} - -moduleBaseFunction(moduleAssetBatchGetOnLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &b->batch->onLoaded, "_onLoaded" - ); -} - -moduleBaseFunction(moduleAssetBatchGetOnEntryLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &b->batch->onEntryLoaded, "_onEntryLoaded" - ); -} - -moduleBaseFunction(moduleAssetBatchGetOnError) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &b->batch->onError, "_onError" - ); -} - -moduleBaseFunction(moduleAssetBatchGetOnEntryError) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &b->batch->onEntryError, "_onEntryError" - ); -} - -moduleBaseFunction(moduleAssetBatchToString) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_string_sz("AssetBatch:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "AssetBatch(%u)", (unsigned)b->batch->count); - return jerry_string_sz(buf); -} - -void moduleAssetBatchInit(void) { - ASSET_BATCH_PEND_COUNT = 0; - scriptProtoInit( - &MODULE_ASSET_BATCH_PROTO, "AssetBatch", - sizeof(jsassetbatch_t), moduleAssetBatchCtor - ); - MODULE_ASSET_BATCH_PROTO.info.free_cb = moduleAssetBatchFree; - - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "count", - moduleAssetBatchGetCount, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "isLoaded", - moduleAssetBatchGetIsLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "hasError", - moduleAssetBatchGetHasError, NULL - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "requireLoaded", - moduleAssetBatchRequireLoaded - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "loaded", moduleAssetBatchLoaded - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "lock", moduleAssetBatchLock - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "unlock", moduleAssetBatchUnlock - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "entry", moduleAssetBatchEntry - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "getAssetByPath", - moduleAssetBatchGetAssetByPath - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "onLoaded", - moduleAssetBatchGetOnLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "onEntryLoaded", - moduleAssetBatchGetOnEntryLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "onError", - moduleAssetBatchGetOnError, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "onEntryError", - moduleAssetBatchGetOnEntryError, NULL - ); - scriptProtoDefineToString( - &MODULE_ASSET_BATCH_PROTO, moduleAssetBatchToString - ); -} - -void moduleAssetBatchDispose(void) { - for(uint32_t i = 0; i < ASSET_BATCH_PEND_COUNT; i++) { - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(ASSET_BATCH_PEND[j].key == ASSET_BATCH_PEND[i].key) { - seen = true; break; - } - } - if(!seen) { - assetbatch_t *b = (assetbatch_t *)ASSET_BATCH_PEND[i].key; - eventUnsubscribe(&b->onLoaded, assetBatchOnLoadedFire); - eventUnsubscribe(&b->onError, assetBatchOnErrorFire); - } - } - scriptPromisePendFreeAll(ASSET_BATCH_PEND, &ASSET_BATCH_PEND_COUNT); - scriptProtoDispose(&MODULE_ASSET_BATCH_PROTO); -} diff --git a/src/dusk/script/module/asset/moduleassetbatch.h b/src/dusk/script/module/asset/moduleassetbatch.h deleted file mode 100644 index 4067594c..00000000 --- a/src/dusk/script/module/asset/moduleassetbatch.h +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/scriptpromisepend.h" -#include "script/module/asset/moduleassetentry.h" -#include "asset/assetbatch.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "util/memory.h" - -extern scriptproto_t MODULE_ASSET_BATCH_PROTO; - -/** C struct wrapped by every AssetBatch JS instance. */ -typedef struct { - assetbatch_t *batch; -} jsassetbatch_t; - -/** - * Resolves all pending loaded() promises for this batch, then - * unsubscribes from both onLoaded and onError. - * - * @param params Unused. - * @param user The assetbatch_t pointer that finished loading. - */ -void assetBatchOnLoadedFire(void *params, void *user); - -/** - * Rejects all pending loaded() promises for this batch, then - * unsubscribes from both onLoaded and onError. - * - * @param params Unused. - * @param user The assetbatch_t pointer that errored. - */ -void assetBatchOnErrorFire(void *params, void *user); - -/** - * GC free callback - disposes and frees the batch when the JS object is - * garbage collected. - * - * @param ptr Native jsassetbatch_t pointer. - * @param info Native info (unused). - */ -void moduleAssetBatchFree(void *ptr, jerry_object_native_info_t *info); - -/** - * Returns the jsassetbatch_t pointer from the current this_value. - * - * @param callInfo The call info. - * @return Pointer to the jsassetbatch_t, or NULL if invalid. - */ -jsassetbatch_t *moduleAssetBatchSelf(const jerry_call_info_t *callInfo); - -/** - * AssetBatch(descriptors[]) constructor. Parses an array of - * {path, type, format?/input?/axis?} descriptor objects, locks all assets, - * and returns an AssetBatch JS object. Works with and without `new`. - * - * @param args[0] Array of AssetBatchDescriptor objects. - */ -moduleBaseFunction(moduleAssetBatchCtor); - -/** @return Number of entries in the batch. */ -moduleBaseFunction(moduleAssetBatchGetCount); - -/** @return True when every entry has reached LOADED. */ -moduleBaseFunction(moduleAssetBatchGetIsLoaded); - -/** @return True if any entry is in an ERROR state. */ -moduleBaseFunction(moduleAssetBatchGetHasError); - -/** - * requireLoaded() - blocks until every entry is loaded. - * @return this for chaining. - * @throws If any entry fails to load. - */ -moduleBaseFunction(moduleAssetBatchRequireLoaded); - -/** - * loaded() - returns a Promise that resolves when all entries load, or - * rejects if any entry errors. Resolves immediately if already loaded. - */ -moduleBaseFunction(moduleAssetBatchLoaded); - -/** - * lock() - acquires one additional lock on every entry. - * @return this for chaining. - */ -moduleBaseFunction(moduleAssetBatchLock); - -/** - * unlock() - releases all locks and disposes the batch. The object is - * invalid after this call. - */ -moduleBaseFunction(moduleAssetBatchUnlock); - -/** - * entry(index) - returns the AssetEntry at index, adding an independent - * lock. - * @param args[0] Index (number). - * @return AssetEntry, or undefined if out of range. - */ -moduleBaseFunction(moduleAssetBatchEntry); - -/** - * getAssetByPath(path) - finds the first entry whose name matches path - * and returns it as a locked AssetEntry, or undefined if not found. - * The returned entry must be unlocked separately when no longer needed. - * @param args[0] Path string to match against entry names. - * @return AssetEntry, or undefined if no entry matches. - */ -moduleBaseFunction(moduleAssetBatchGetAssetByPath); - -/** @return The onLoaded Event (fires once when all entries load). */ -moduleBaseFunction(moduleAssetBatchGetOnLoaded); - -/** @return The onEntryLoaded Event (fires per entry). */ -moduleBaseFunction(moduleAssetBatchGetOnEntryLoaded); - -/** @return The onError Event (fires once when any entry errors). */ -moduleBaseFunction(moduleAssetBatchGetOnError); - -/** @return The onEntryError Event (fires per errored entry). */ -moduleBaseFunction(moduleAssetBatchGetOnEntryError); - -/** @return "AssetBatch(n)" string. */ -moduleBaseFunction(moduleAssetBatchToString); - -/** - * Initializes the AssetBatch module and registers the global AssetBatch - * class. - */ -void moduleAssetBatchInit(void); - -/** Disposes the AssetBatch module. */ -void moduleAssetBatchDispose(void); diff --git a/src/dusk/script/module/asset/moduleassetentry.c b/src/dusk/script/module/asset/moduleassetentry.c deleted file mode 100644 index 0b228378..00000000 --- a/src/dusk/script/module/asset/moduleassetentry.c +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleassetentry.h" - -#define ASSET_ENTRY_PEND_MAX 16 - -scriptproto_t MODULE_ASSET_ENTRY_PROTO; - -static scriptpromisepend_t ASSET_ENTRY_PEND[ASSET_ENTRY_PEND_MAX]; -static uint32_t ASSET_ENTRY_PEND_COUNT = 0; - -void assetEntryOnLoadedFire(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)user; - jerry_value_t undef = jerry_undefined(); - uint32_t n = scriptPromisePendResolve( - ASSET_ENTRY_PEND, &ASSET_ENTRY_PEND_COUNT, entry, undef - ); - jerry_value_free(undef); - if(!n) return; - eventUnsubscribe(&entry->onLoaded, assetEntryOnLoadedFire); - eventUnsubscribe(&entry->onError, assetEntryOnErrorFire); -} - -void assetEntryOnErrorFire(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)user; - jerry_value_t err = jerry_string_sz("Asset failed to load"); - uint32_t n = scriptPromisePendReject( - ASSET_ENTRY_PEND, &ASSET_ENTRY_PEND_COUNT, entry, err - ); - jerry_value_free(err); - if(!n) return; - eventUnsubscribe(&entry->onLoaded, assetEntryOnLoadedFire); - eventUnsubscribe(&entry->onError, assetEntryOnErrorFire); -} - -void moduleAssetEntryFree(void *ptr, jerry_object_native_info_t *info) { - jsassetentry_t *e = (jsassetentry_t *)ptr; - if(e && e->entry) { - assetUnlockEntry(e->entry); - e->entry = NULL; - } - memoryFree(ptr); -} - -jsassetentry_t *moduleAssetEntrySelf(const jerry_call_info_t *callInfo) { - return (jsassetentry_t *)scriptProtoGetValue( - &MODULE_ASSET_ENTRY_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(moduleAssetEntryCtor) { - return moduleBaseThrow("AssetEntry cannot be instantiated with new"); -} - -moduleBaseFunction(moduleAssetEntryGetName) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return jerry_string_sz(e->entry->name); -} - -moduleBaseFunction(moduleAssetEntryGetState) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return jerry_number((double)e->entry->state); -} - -moduleBaseFunction(moduleAssetEntryGetType) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return jerry_number((double)e->entry->type); -} - -moduleBaseFunction(moduleAssetEntryGetIsLoaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_boolean(false); - return jerry_boolean(e->entry->state == ASSET_ENTRY_STATE_LOADED); -} - -moduleBaseFunction(moduleAssetEntryGetTexture) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - if(e->entry->type != ASSET_LOADER_TYPE_TEXTURE) return jerry_undefined(); - if(e->entry->state != ASSET_ENTRY_STATE_LOADED) return jerry_undefined(); - assetEntryLock(e->entry); - jstexture_t tex = { .entry = e->entry }; - return scriptProtoCreateValue(&MODULE_TEXTURE_PROTO, &tex); -} - -moduleBaseFunction(moduleAssetEntryRequireLoaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) { - return moduleBaseThrow("AssetEntry.requireLoaded: invalid entry"); - } - errorret_t err = assetRequireLoaded(e->entry); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_value_copy(callInfo->this_value); -} - -moduleBaseFunction(moduleAssetEntryUnlock) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - assetUnlockEntry(e->entry); - e->entry = NULL; - return jerry_undefined(); -} - -moduleBaseFunction(moduleAssetEntryGetOnLoaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &e->entry->onLoaded, "_onLoaded" - ); -} - -moduleBaseFunction(moduleAssetEntryGetOnUnloaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &e->entry->onUnloaded, "_onUnloaded" - ); -} - -moduleBaseFunction(moduleAssetEntryGetOnError) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &e->entry->onError, "_onError" - ); -} - -moduleBaseFunction(moduleAssetEntryLoaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) { - return moduleBaseThrow("AssetEntry.loaded: invalid entry"); - } - jerry_value_t promise = jerry_promise(); - if(e->entry->state == ASSET_ENTRY_STATE_LOADED) { - jerry_value_t undef = jerry_undefined(); - jerry_value_t r = jerry_promise_resolve(promise, undef); - jerry_value_free(undef); - jerry_value_free(r); - return promise; - } - if(e->entry->state == ASSET_ENTRY_STATE_ERROR) { - jerry_value_t err = jerry_string_sz("Asset failed to load"); - jerry_value_t r = jerry_promise_reject(promise, err); - jerry_value_free(err); - jerry_value_free(r); - return promise; - } - if(ASSET_ENTRY_PEND_COUNT >= ASSET_ENTRY_PEND_MAX) { - jerry_value_free(promise); - return moduleBaseThrow("AssetEntry.loaded: too many pending"); - } - if(!scriptPromisePendHas( - ASSET_ENTRY_PEND, ASSET_ENTRY_PEND_COUNT, e->entry - )) { - eventSubscribe( - &e->entry->onLoaded, assetEntryOnLoadedFire, e->entry - ); - eventSubscribe( - &e->entry->onError, assetEntryOnErrorFire, e->entry - ); - } - scriptPromisePendAdd( - ASSET_ENTRY_PEND, &ASSET_ENTRY_PEND_COUNT, - ASSET_ENTRY_PEND_MAX, e->entry, promise - ); - return promise; -} - -moduleBaseFunction(moduleAssetEntryToString) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_string_sz("AssetEntry:invalid"); - char_t buf[64]; - snprintf(buf, sizeof(buf), "AssetEntry(%s)", e->entry->name); - return jerry_string_sz(buf); -} - -void moduleAssetEntryInit(void) { - ASSET_ENTRY_PEND_COUNT = 0; - scriptProtoInit( - &MODULE_ASSET_ENTRY_PROTO, "AssetEntry", - sizeof(jsassetentry_t), moduleAssetEntryCtor - ); - MODULE_ASSET_ENTRY_PROTO.info.free_cb = moduleAssetEntryFree; - - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "name", moduleAssetEntryGetName, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "state", moduleAssetEntryGetState, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "type", moduleAssetEntryGetType, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "isLoaded", - moduleAssetEntryGetIsLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "texture", - moduleAssetEntryGetTexture, NULL - ); - scriptProtoDefineFunc( - &MODULE_ASSET_ENTRY_PROTO, "requireLoaded", - moduleAssetEntryRequireLoaded - ); - scriptProtoDefineFunc( - &MODULE_ASSET_ENTRY_PROTO, "unlock", moduleAssetEntryUnlock - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "onLoaded", - moduleAssetEntryGetOnLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "onUnloaded", - moduleAssetEntryGetOnUnloaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "onError", - moduleAssetEntryGetOnError, NULL - ); - scriptProtoDefineFunc( - &MODULE_ASSET_ENTRY_PROTO, "loaded", moduleAssetEntryLoaded - ); - scriptProtoDefineToString( - &MODULE_ASSET_ENTRY_PROTO, moduleAssetEntryToString - ); - - jerry_value_t ctor = MODULE_ASSET_ENTRY_PROTO.constructor; - struct { const char_t *name; int val; } states[] = { - { "NOT_STARTED", ASSET_ENTRY_STATE_NOT_STARTED }, - { "PENDING", ASSET_ENTRY_STATE_PENDING_ASYNC }, - { "LOADING", ASSET_ENTRY_STATE_LOADING_ASYNC }, - { "LOADED", ASSET_ENTRY_STATE_LOADED }, - { "ERROR", ASSET_ENTRY_STATE_ERROR }, - }; - for(int i = 0; i < 5; i++) { - jerry_value_t k = jerry_string_sz(states[i].name); - jerry_value_t v = jerry_number((double)states[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleAssetEntryDispose(void) { - for(uint32_t i = 0; i < ASSET_ENTRY_PEND_COUNT; i++) { - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(ASSET_ENTRY_PEND[j].key == ASSET_ENTRY_PEND[i].key) { - seen = true; break; - } - } - if(!seen) { - assetentry_t *e = (assetentry_t *)ASSET_ENTRY_PEND[i].key; - eventUnsubscribe(&e->onLoaded, assetEntryOnLoadedFire); - eventUnsubscribe(&e->onError, assetEntryOnErrorFire); - } - } - scriptPromisePendFreeAll(ASSET_ENTRY_PEND, &ASSET_ENTRY_PEND_COUNT); - scriptProtoDispose(&MODULE_ASSET_ENTRY_PROTO); -} diff --git a/src/dusk/script/module/asset/moduleassetentry.h b/src/dusk/script/module/asset/moduleassetentry.h deleted file mode 100644 index 096fae60..00000000 --- a/src/dusk/script/module/asset/moduleassetentry.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/scriptpromisepend.h" -#include "script/module/display/moduletexture.h" -#include "script/module/event/moduleevent.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "asset/loader/assetentry.h" -#include "util/memory.h" - -extern scriptproto_t MODULE_ASSET_ENTRY_PROTO; - -/** C struct wrapped by every AssetEntry JS instance. */ -typedef struct { - assetentry_t *entry; -} jsassetentry_t; - -/** - * Resolves all pending loaded() promises for this entry, then - * unsubscribes from both onLoaded and onError. - * - * @param params Unused. - * @param user The assetentry_t pointer that finished loading. - */ -void assetEntryOnLoadedFire(void *params, void *user); - -/** - * Rejects all pending loaded() promises for this entry, then - * unsubscribes from both onLoaded and onError. - * - * @param params Unused. - * @param user The assetentry_t pointer that errored. - */ -void assetEntryOnErrorFire(void *params, void *user); - -/** - * GC free callback - releases the asset lock when the AssetEntry JS object - * is garbage collected. - * - * @param ptr Native jsassetentry_t pointer. - * @param info Native info (unused). - */ -void moduleAssetEntryFree(void *ptr, jerry_object_native_info_t *info); - -/** - * Returns the jsassetentry_t pointer from the current this_value. - * - * @param callInfo The call info. - * @return Pointer to the jsassetentry_t, or NULL if invalid. - */ -jsassetentry_t *moduleAssetEntrySelf(const jerry_call_info_t *callInfo); - -/** AssetEntry() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleAssetEntryCtor); - -/** @return Archive-relative path used as the cache key. */ -moduleBaseFunction(moduleAssetEntryGetName); - -/** @return Current loading state as a number (AssetEntry.* constants). */ -moduleBaseFunction(moduleAssetEntryGetState); - -/** @return Loader type constant. */ -moduleBaseFunction(moduleAssetEntryGetType); - -/** @return True when the entry has fully loaded. */ -moduleBaseFunction(moduleAssetEntryGetIsLoaded); - -/** - * Returns a Texture wrapping this entry's texture data. The Texture holds - * its own asset lock independently of this AssetEntry. - * @return A Texture JS object, or undefined if not a loaded texture. - */ -moduleBaseFunction(moduleAssetEntryGetTexture); - -/** - * requireLoaded() - blocks until the entry is LOADED or ERROR. - * @return this for chaining. - * @throws If the load fails. - */ -moduleBaseFunction(moduleAssetEntryRequireLoaded); - -/** unlock() - releases the asset lock immediately. */ -moduleBaseFunction(moduleAssetEntryUnlock); - -/** @return The onLoaded Event for this entry. */ -moduleBaseFunction(moduleAssetEntryGetOnLoaded); - -/** @return The onUnloaded Event for this entry. */ -moduleBaseFunction(moduleAssetEntryGetOnUnloaded); - -/** @return The onError Event for this entry. */ -moduleBaseFunction(moduleAssetEntryGetOnError); - -/** - * loaded() - returns a Promise that resolves when the entry loads, or - * rejects on error. Resolves immediately if already loaded. - */ -moduleBaseFunction(moduleAssetEntryLoaded); - -/** @return "AssetEntry(name)" string. */ -moduleBaseFunction(moduleAssetEntryToString); - -/** Initializes the AssetEntry module. */ -void moduleAssetEntryInit(void); - -/** Disposes the AssetEntry module. */ -void moduleAssetEntryDispose(void); diff --git a/src/dusk/script/module/asset/moduleeventproxy.c b/src/dusk/script/module/asset/moduleeventproxy.c deleted file mode 100644 index fdfc965d..00000000 --- a/src/dusk/script/module/asset/moduleeventproxy.c +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleeventproxy.h" -#include "util/memory.h" - -scriptproto_t MODULE_EVENT_PROXY_PROTO; - -void moduleEventProxyTrampoline0(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventProxyTrampoline1(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventProxyTrampoline2(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventProxyTrampoline3(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -eventcallback_t MODULE_EVENT_PROXY_TRAMPOLINES[MODULE_EVENT_PROXY_MAX_SLOTS] = { - moduleEventProxyTrampoline0, - moduleEventProxyTrampoline1, - moduleEventProxyTrampoline2, - moduleEventProxyTrampoline3, -}; - -void moduleEventProxyFree(void *ptr, jerry_object_native_info_t *info) { - jseventproxy_t *ep = (jseventproxy_t *)ptr; - if(ep) { - for(uint32_t i = 0; i < MODULE_EVENT_PROXY_MAX_SLOTS; i++) { - if(jerry_value_is_function(ep->fns[i])) { - if(ep->event) { - eventUnsubscribe(ep->event, MODULE_EVENT_PROXY_TRAMPOLINES[i]); - } - jerry_value_free(ep->fns[i]); - } - } - } - memoryFree(ptr); -} - -jseventproxy_t *moduleEventProxySelf(const jerry_call_info_t *callInfo) { - return (jseventproxy_t *)scriptProtoGetValue( - &MODULE_EVENT_PROXY_PROTO, callInfo->this_value - ); -} - -static jerry_value_t moduleEventProxyGetSlot( - const jerry_call_info_t *callInfo, - const uint32_t slot -) { - jseventproxy_t *ep = moduleEventProxySelf(callInfo); - if(!ep || !ep->event || slot >= ep->event->size) return jerry_null(); - return jerry_value_is_function(ep->fns[slot]) - ? jerry_value_copy(ep->fns[slot]) - : jerry_null(); -} - -static jerry_value_t moduleEventProxySetSlot( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc, - const uint32_t slot -) { - jseventproxy_t *ep = moduleEventProxySelf(callInfo); - if(!ep || !ep->event || slot >= ep->event->size) return jerry_undefined(); - - if(jerry_value_is_function(ep->fns[slot])) { - eventUnsubscribe(ep->event, MODULE_EVENT_PROXY_TRAMPOLINES[slot]); - jerry_value_free(ep->fns[slot]); - ep->fns[slot] = jerry_undefined(); - } - - jerry_value_t val = (argc > 0) ? args[0] : jerry_undefined(); - if(jerry_value_is_function(val)) { - ep->fns[slot] = jerry_value_copy(val); - eventSubscribe( - ep->event, - MODULE_EVENT_PROXY_TRAMPOLINES[slot], - (void *)(uintptr_t)ep->fns[slot] - ); - } - return jerry_undefined(); -} - -moduleBaseFunction(moduleEventProxyGet0) { - return moduleEventProxyGetSlot(callInfo, 0); -} -moduleBaseFunction(moduleEventProxySet0) { - return moduleEventProxySetSlot(callInfo, args, argc, 0); -} - -moduleBaseFunction(moduleEventProxyGet1) { - return moduleEventProxyGetSlot(callInfo, 1); -} -moduleBaseFunction(moduleEventProxySet1) { - return moduleEventProxySetSlot(callInfo, args, argc, 1); -} - -moduleBaseFunction(moduleEventProxyGet2) { - return moduleEventProxyGetSlot(callInfo, 2); -} -moduleBaseFunction(moduleEventProxySet2) { - return moduleEventProxySetSlot(callInfo, args, argc, 2); -} - -moduleBaseFunction(moduleEventProxyGet3) { - return moduleEventProxyGetSlot(callInfo, 3); -} -moduleBaseFunction(moduleEventProxySet3) { - return moduleEventProxySetSlot(callInfo, args, argc, 3); -} - -moduleBaseFunction(moduleEventProxyGetLength) { - jseventproxy_t *ep = moduleEventProxySelf(callInfo); - if(!ep || !ep->event) return jerry_number(0.0); - return jerry_number((double)ep->event->size); -} - -moduleBaseFunction(moduleEventProxyToString) { - return jerry_string_sz("EventProxy"); -} - -jerry_value_t moduleEventProxyGetOrCreate( - const jerry_call_info_t *callInfo, - event_t *event, - const char_t *pinKey -) { - jerry_value_t keyStr = jerry_string_sz(pinKey); - jerry_value_t existing = jerry_object_get(callInfo->this_value, keyStr); - if(!jerry_value_is_undefined(existing)) { - jerry_value_free(keyStr); - return existing; - } - jerry_value_free(existing); - - jseventproxy_t ep; - ep.event = event; - for(uint32_t i = 0; i < MODULE_EVENT_PROXY_MAX_SLOTS; i++) { - ep.fns[i] = jerry_undefined(); - } - - jerry_value_t proxy = scriptProtoCreateValue( - &MODULE_EVENT_PROXY_PROTO, &ep - ); - jerry_object_set(callInfo->this_value, keyStr, proxy); - jerry_value_free(keyStr); - return proxy; -} - -void moduleEventProxyInit(void) { - scriptProtoInit( - &MODULE_EVENT_PROXY_PROTO, NULL, - sizeof(jseventproxy_t), NULL - ); - MODULE_EVENT_PROXY_PROTO.info.free_cb = moduleEventProxyFree; - - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "0", - moduleEventProxyGet0, moduleEventProxySet0 - ); - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "1", - moduleEventProxyGet1, moduleEventProxySet1 - ); - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "2", - moduleEventProxyGet2, moduleEventProxySet2 - ); - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "3", - moduleEventProxyGet3, moduleEventProxySet3 - ); - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "length", - moduleEventProxyGetLength, NULL - ); - scriptProtoDefineToString( - &MODULE_EVENT_PROXY_PROTO, moduleEventProxyToString - ); -} - -void moduleEventProxyDispose(void) { - scriptProtoDispose(&MODULE_EVENT_PROXY_PROTO); -} diff --git a/src/dusk/script/module/asset/moduleeventproxy.h b/src/dusk/script/module/asset/moduleeventproxy.h deleted file mode 100644 index f1c785b2..00000000 --- a/src/dusk/script/module/asset/moduleeventproxy.h +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/* Merged into script/module/event/moduleevent.h */ -#pragma once -#include "script/module/event/moduleevent.h" diff --git a/src/dusk/script/module/console/CMakeLists.txt b/src/dusk/script/module/console/CMakeLists.txt deleted file mode 100644 index 9867bfde..00000000 --- a/src/dusk/script/module/console/CMakeLists.txt +++ /dev/null @@ -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 - moduleconsole.c -) diff --git a/src/dusk/script/module/console/moduleconsole.c b/src/dusk/script/module/console/moduleconsole.c deleted file mode 100644 index 15fd17ed..00000000 --- a/src/dusk/script/module/console/moduleconsole.c +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleconsole.h" -#include "util/string.h" -#include - -scriptproto_t MODULE_CONSOLE_PROTO; - -moduleBaseFunction(moduleConsolePrint) { - char_t buf[512]; - char_t msg[4096]; - size_t msgLen = 0; - - for(jerry_length_t i = 0; i < argc; ++i) { - jerry_value_t strVal = jerry_value_to_string(args[i]); - moduleBaseToString(strVal, buf, sizeof(buf)); - jerry_value_free(strVal); - - size_t partLen = strlen(buf); - if(msgLen + partLen + 1 < sizeof(msg)) { - stringCopy(msg + msgLen, buf, sizeof(msg) - msgLen); - msgLen += partLen; - } - - if(i + 1 < argc && msgLen + 1 < sizeof(msg)) { - msg[msgLen++] = '\t'; - msg[msgLen] = '\0'; - } - } - - consolePrint("%s", msg); - return jerry_undefined(); -} - -moduleBaseFunction(moduleConsoleGetVisible) { - return jerry_boolean(CONSOLE.visible); -} - -moduleBaseFunction(moduleConsoleSetVisible) { - moduleBaseRequireArgs(1); - CONSOLE.visible = moduleBaseArgBool(0); - return jerry_undefined(); -} - -void moduleConsoleInit(void) { - scriptProtoInit( - &MODULE_CONSOLE_PROTO, "Console", - sizeof(uint8_t), NULL - ); - scriptProtoDefineStaticFunc( - &MODULE_CONSOLE_PROTO, "print", moduleConsolePrint - ); - scriptProtoDefineStaticProp( - &MODULE_CONSOLE_PROTO, "visible", - moduleConsoleGetVisible, moduleConsoleSetVisible - ); -} - -void moduleConsoleDispose(void) { - scriptProtoDispose(&MODULE_CONSOLE_PROTO); -} diff --git a/src/dusk/script/module/console/moduleconsole.h b/src/dusk/script/module/console/moduleconsole.h deleted file mode 100644 index 8388cecd..00000000 --- a/src/dusk/script/module/console/moduleconsole.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "console/console.h" - -extern scriptproto_t MODULE_CONSOLE_PROTO; - -/** - * Console.print(...args) - concatenates all args tab-separated and prints - * to the engine console. - */ -moduleBaseFunction(moduleConsolePrint); - -/** @return Whether the console overlay is currently visible. */ -moduleBaseFunction(moduleConsoleGetVisible); - -/** Sets console visibility. @param args[0] Boolean visible state. */ -moduleBaseFunction(moduleConsoleSetVisible); - -/** - * Initializes the Console module and registers the global Console object with - * print() and the visible property. - */ -void moduleConsoleInit(void); - -/** - * Disposes the Console module. - */ -void moduleConsoleDispose(void); diff --git a/src/dusk/script/module/display/CMakeLists.txt b/src/dusk/script/module/display/CMakeLists.txt deleted file mode 100644 index f009d77d..00000000 --- a/src/dusk/script/module/display/CMakeLists.txt +++ /dev/null @@ -1,11 +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 - modulecolor.c - modulescreen.c - moduletexture.c -) diff --git a/src/dusk/script/module/display/modulecolor.c b/src/dusk/script/module/display/modulecolor.c deleted file mode 100644 index 09d77127..00000000 --- a/src/dusk/script/module/display/modulecolor.c +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecolor.h" - -scriptproto_t MODULE_COLOR_PROTO; - -color_t *moduleColorFrom(const jerry_value_t val) { - return (color_t *)scriptProtoGetValue(&MODULE_COLOR_PROTO, val); -} - -jerry_value_t moduleColorPush(const color_t c) { - return scriptProtoCreateValue(&MODULE_COLOR_PROTO, &c); -} - -moduleBaseFunction(moduleColorConstructor) { - color_t *ptr = (color_t *)memoryAllocate(sizeof(color_t)); - ptr->r = (uint8_t)moduleBaseOptInt(0, 0); - ptr->g = (uint8_t)moduleBaseOptInt(1, 0); - ptr->b = (uint8_t)moduleBaseOptInt(2, 0); - ptr->a = (uint8_t)moduleBaseOptInt(3, 255); - jerry_object_set_native_ptr( - callInfo->this_value, &MODULE_COLOR_PROTO.info, ptr - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorGetR) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - return jerry_number((double)c->r); -} - -moduleBaseFunction(moduleColorSetR) { - moduleBaseRequireArgs(1); - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - c->r = (uint8_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorGetG) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - return jerry_number((double)c->g); -} - -moduleBaseFunction(moduleColorSetG) { - moduleBaseRequireArgs(1); - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - c->g = (uint8_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorGetB) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - return jerry_number((double)c->b); -} - -moduleBaseFunction(moduleColorSetB) { - moduleBaseRequireArgs(1); - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - c->b = (uint8_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorGetA) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - return jerry_number((double)c->a); -} - -moduleBaseFunction(moduleColorSetA) { - moduleBaseRequireArgs(1); - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - c->a = (uint8_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorToString) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_string_sz("Color:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Color(%u,%u,%u,%u)", - (unsigned)c->r, (unsigned)c->g, - (unsigned)c->b, (unsigned)c->a - ); - return jerry_string_sz(buf); -} - -void moduleColorInit(void) { - scriptProtoInit( - &MODULE_COLOR_PROTO, "Color", - sizeof(color_t), moduleColorConstructor - ); - scriptProtoDefineProp( - &MODULE_COLOR_PROTO, "r", moduleColorGetR, moduleColorSetR - ); - scriptProtoDefineProp( - &MODULE_COLOR_PROTO, "g", moduleColorGetG, moduleColorSetG - ); - scriptProtoDefineProp( - &MODULE_COLOR_PROTO, "b", moduleColorGetB, moduleColorSetB - ); - scriptProtoDefineProp( - &MODULE_COLOR_PROTO, "a", moduleColorGetA, moduleColorSetA - ); - scriptProtoDefineToString(&MODULE_COLOR_PROTO, moduleColorToString); - - struct { const char_t *name; color_t val; } constants[] = { - { "WHITE", COLOR_WHITE }, - { "BLACK", COLOR_BLACK }, - { "RED", COLOR_RED }, - { "GREEN", COLOR_GREEN }, - { "BLUE", COLOR_BLUE }, - { "YELLOW", COLOR_YELLOW }, - { "CYAN", COLOR_CYAN }, - { "MAGENTA", COLOR_MAGENTA }, - { "TRANSPARENT", COLOR_TRANSPARENT }, - { "GRAY", COLOR_GRAY }, - { "LIGHT_GRAY", COLOR_LIGHT_GRAY }, - { "DARK_GRAY", COLOR_DARK_GRAY }, - { "ORANGE", COLOR_ORANGE }, - { "PURPLE", COLOR_PURPLE }, - { "PINK", COLOR_PINK }, - { "TEAL", COLOR_TEAL }, - { "CORNFLOWER_BLUE", COLOR_CORNFLOWER_BLUE }, - }; - jerry_value_t ctor = MODULE_COLOR_PROTO.constructor; - for(int i = 0; i < (int)(sizeof(constants)/sizeof(constants[0])); i++) { - jerry_value_t k = jerry_string_sz(constants[i].name); - jerry_value_t v = moduleColorPush(constants[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleColorDispose(void) { - scriptProtoDispose(&MODULE_COLOR_PROTO); -} diff --git a/src/dusk/script/module/display/modulecolor.h b/src/dusk/script/module/display/modulecolor.h deleted file mode 100644 index df92f23e..00000000 --- a/src/dusk/script/module/display/modulecolor.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "util/memory.h" -#include "display/color.h" - -extern scriptproto_t MODULE_COLOR_PROTO; - -/** - * Returns the native color_t pointer from a Color JS value. - * - * @param val The JS value to extract from. - * @return Pointer to the color data, or NULL if not a Color. - */ -color_t *moduleColorFrom(const jerry_value_t val); - -/** - * Creates a Color JS object wrapping a copy of a C color_t. - * - * @param c The source color. - * @return A new Color JS object. - */ -jerry_value_t moduleColorPush(const color_t c); - -/** Color(r?, g?, b?, a?) constructor. */ -moduleBaseFunction(moduleColorConstructor); - -/** @return The red channel as a number. */ -moduleBaseFunction(moduleColorGetR); -/** Sets the red channel. @param args[0] New r value (0-255). */ -moduleBaseFunction(moduleColorSetR); - -/** @return The green channel as a number. */ -moduleBaseFunction(moduleColorGetG); -/** Sets the green channel. @param args[0] New g value (0-255). */ -moduleBaseFunction(moduleColorSetG); - -/** @return The blue channel as a number. */ -moduleBaseFunction(moduleColorGetB); -/** Sets the blue channel. @param args[0] New b value (0-255). */ -moduleBaseFunction(moduleColorSetB); - -/** @return The alpha channel as a number. */ -moduleBaseFunction(moduleColorGetA); -/** Sets the alpha channel. @param args[0] New a value (0-255). */ -moduleBaseFunction(moduleColorSetA); - -/** @return "Color(r,g,b,a)" string. */ -moduleBaseFunction(moduleColorToString); - -/** - * Initializes the Color module and registers the global Color class with named - * color constants (Color.WHITE, Color.RED, etc.). - */ -void moduleColorInit(void); - -/** - * Disposes the Color module. - */ -void moduleColorDispose(void); diff --git a/src/dusk/script/module/display/modulescreen.c b/src/dusk/script/module/display/modulescreen.c deleted file mode 100644 index 71842da7..00000000 --- a/src/dusk/script/module/display/modulescreen.c +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulescreen.h" - -scriptproto_t MODULE_SCREEN_PROTO; - -moduleBaseFunction(moduleScreenGetWidth) { - return jerry_number((double)SCREEN.width); -} - -moduleBaseFunction(moduleScreenGetHeight) { - return jerry_number((double)SCREEN.height); -} - -moduleBaseFunction(moduleScreenGetAspect) { - return jerry_number((double)SCREEN.aspect); -} - -void moduleScreenInit(void) { - scriptProtoInit(&MODULE_SCREEN_PROTO, "Screen", sizeof(uint8_t), NULL); - scriptProtoDefineStaticProp( - &MODULE_SCREEN_PROTO, "width", moduleScreenGetWidth, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_SCREEN_PROTO, "height", moduleScreenGetHeight, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_SCREEN_PROTO, "aspect", moduleScreenGetAspect, NULL - ); -} - -void moduleScreenDispose(void) { - scriptProtoDispose(&MODULE_SCREEN_PROTO); -} diff --git a/src/dusk/script/module/display/modulescreen.h b/src/dusk/script/module/display/modulescreen.h deleted file mode 100644 index e15a3c03..00000000 --- a/src/dusk/script/module/display/modulescreen.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "display/screen/screen.h" - -extern scriptproto_t MODULE_SCREEN_PROTO; - -/** @return Current screen width in pixels. */ -moduleBaseFunction(moduleScreenGetWidth); - -/** @return Current screen height in pixels. */ -moduleBaseFunction(moduleScreenGetHeight); - -/** @return Current screen aspect ratio (width / height). */ -moduleBaseFunction(moduleScreenGetAspect); - -/** - * Initializes the Screen module and registers read-only width/height/aspect - * properties on the global Screen object. - */ -void moduleScreenInit(void); - -/** - * Disposes the Screen module. - */ -void moduleScreenDispose(void); diff --git a/src/dusk/script/module/display/moduletexture.c b/src/dusk/script/module/display/moduletexture.c deleted file mode 100644 index e787e103..00000000 --- a/src/dusk/script/module/display/moduletexture.c +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletexture.h" - -scriptproto_t MODULE_TEXTURE_PROTO; - -void moduleTextureFree(void *ptr, jerry_object_native_info_t *info) { - jstexture_t *tex = (jstexture_t *)ptr; - if(tex && tex->entry) { - assetUnlockEntry(tex->entry); - tex->entry = NULL; - } - memoryFree(ptr); -} - -jstexture_t *moduleTextureSelf(const jerry_call_info_t *callInfo) { - return (jstexture_t *)scriptProtoGetValue( - &MODULE_TEXTURE_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(moduleTextureCtor) { - return moduleBaseThrow("Texture cannot be instantiated with new"); -} - -moduleBaseFunction(moduleTextureGetWidth) { - jstexture_t *t = moduleTextureSelf(callInfo); - if(!t || !t->entry) return jerry_undefined(); - return jerry_number((double)t->entry->data.texture.width); -} - -moduleBaseFunction(moduleTextureGetHeight) { - jstexture_t *t = moduleTextureSelf(callInfo); - if(!t || !t->entry) return jerry_undefined(); - return jerry_number((double)t->entry->data.texture.height); -} - -moduleBaseFunction(moduleTextureToString) { - jstexture_t *t = moduleTextureSelf(callInfo); - if(!t || !t->entry) return jerry_string_sz("Texture:invalid"); - char_t buf[64]; - snprintf(buf, sizeof(buf), "Texture(%dx%d)", - t->entry->data.texture.width, - t->entry->data.texture.height - ); - return jerry_string_sz(buf); -} - -void moduleTextureInit(void) { - scriptProtoInit( - &MODULE_TEXTURE_PROTO, "Texture", - sizeof(jstexture_t), moduleTextureCtor - ); - MODULE_TEXTURE_PROTO.info.free_cb = moduleTextureFree; - - scriptProtoDefineProp( - &MODULE_TEXTURE_PROTO, "width", moduleTextureGetWidth, NULL - ); - scriptProtoDefineProp( - &MODULE_TEXTURE_PROTO, "height", moduleTextureGetHeight, NULL - ); - scriptProtoDefineToString(&MODULE_TEXTURE_PROTO, moduleTextureToString); - - jerry_value_t ctor = MODULE_TEXTURE_PROTO.constructor; - struct { const char_t *name; int val; } formats[] = { - { "FORMAT_RGBA", TEXTURE_FORMAT_RGBA }, - { "FORMAT_PALETTE", TEXTURE_FORMAT_PALETTE }, - }; - for(int i = 0; i < 2; i++) { - jerry_value_t k = jerry_string_sz(formats[i].name); - jerry_value_t v = jerry_number((double)formats[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleTextureDispose(void) { - scriptProtoDispose(&MODULE_TEXTURE_PROTO); -} diff --git a/src/dusk/script/module/display/moduletexture.h b/src/dusk/script/module/display/moduletexture.h deleted file mode 100644 index 01159877..00000000 --- a/src/dusk/script/module/display/moduletexture.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "display/texture/texture.h" -#include "util/memory.h" - -extern scriptproto_t MODULE_TEXTURE_PROTO; - -/** C struct wrapped by every Texture JS instance. */ -typedef struct { - assetentry_t *entry; -} jstexture_t; - -/** - * GC free callback - unlocks the asset entry when the Texture JS object is - * garbage collected. - * - * @param ptr Native jstexture_t pointer. - * @param info Native info (unused). - */ -void moduleTextureFree(void *ptr, jerry_object_native_info_t *info); - -/** - * Returns the jstexture_t pointer from the current this_value. - * - * @param callInfo The call info. - * @return Pointer to the jstexture_t, or NULL if invalid. - */ -jstexture_t *moduleTextureSelf(const jerry_call_info_t *callInfo); - -/** Texture() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleTextureCtor); - -/** @return Texture width in pixels, or undefined if not loaded. */ -moduleBaseFunction(moduleTextureGetWidth); - -/** @return Texture height in pixels, or undefined if not loaded. */ -moduleBaseFunction(moduleTextureGetHeight); - -/** @return "Texture(WxH)" string. */ -moduleBaseFunction(moduleTextureToString); - -/** - * Initializes the Texture module and registers the global Texture class with - * FORMAT_RGBA and FORMAT_PALETTE constants. - */ -void moduleTextureInit(void); - -/** - * Disposes the Texture module. - */ -void moduleTextureDispose(void); diff --git a/src/dusk/script/module/engine/CMakeLists.txt b/src/dusk/script/module/engine/CMakeLists.txt deleted file mode 100644 index 87bf0ea6..00000000 --- a/src/dusk/script/module/engine/CMakeLists.txt +++ /dev/null @@ -1,11 +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 - moduleengine.c - moduleframe.c - moduletimeout.c -) diff --git a/src/dusk/script/module/engine/moduleengine.c b/src/dusk/script/module/engine/moduleengine.c deleted file mode 100644 index 9cccc1c7..00000000 --- a/src/dusk/script/module/engine/moduleengine.c +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleengine.h" - -scriptproto_t MODULE_ENGINE_PROTO; - -moduleBaseFunction(moduleEngineGetRunning) { - return jerry_boolean(ENGINE.running); -} - -moduleBaseFunction(moduleEngineExit) { - ENGINE.running = false; - return jerry_undefined(); -} - -void moduleEngineInit(void) { - scriptProtoInit(&MODULE_ENGINE_PROTO, "Engine", sizeof(uint8_t), NULL); - scriptProtoDefineStaticProp( - &MODULE_ENGINE_PROTO, "running", - moduleEngineGetRunning, NULL - ); - scriptProtoDefineStaticFunc( - &MODULE_ENGINE_PROTO, "exit", moduleEngineExit - ); -} - -void moduleEngineDispose(void) { - scriptProtoDispose(&MODULE_ENGINE_PROTO); -} diff --git a/src/dusk/script/module/engine/moduleengine.h b/src/dusk/script/module/engine/moduleengine.h deleted file mode 100644 index 11b7aa3d..00000000 --- a/src/dusk/script/module/engine/moduleengine.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "engine/engine.h" - -extern scriptproto_t MODULE_ENGINE_PROTO; - -/** @return True if the engine main loop is still running. */ -moduleBaseFunction(moduleEngineGetRunning); - -/** Signals the engine to stop on the next frame. */ -moduleBaseFunction(moduleEngineExit); - -/** - * Initializes the Engine module and registers the global Engine object with the - * running property and exit() method. - */ -void moduleEngineInit(void); - -/** - * Disposes the Engine module. - */ -void moduleEngineDispose(void); diff --git a/src/dusk/script/module/engine/moduleframe.c b/src/dusk/script/module/engine/moduleframe.c deleted file mode 100644 index 5ab00613..00000000 --- a/src/dusk/script/module/engine/moduleframe.c +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleframe.h" - -jerry_value_t MODULE_FRAME_PENDING[MODULE_FRAME_PENDING_MAX]; -uint32_t MODULE_FRAME_PENDING_COUNT = 0; - -moduleBaseFunction(moduleFrameFrame) { - if(MODULE_FRAME_PENDING_COUNT >= MODULE_FRAME_PENDING_MAX) { - return moduleBaseThrow("Too many pending frame() calls"); - } - jerry_value_t promise = jerry_promise(); - MODULE_FRAME_PENDING[MODULE_FRAME_PENDING_COUNT++] = - jerry_value_copy(promise); - return promise; -} - -void moduleFrameFlush(void) { - uint32_t count = MODULE_FRAME_PENDING_COUNT; - MODULE_FRAME_PENDING_COUNT = 0; - for(uint32_t i = 0; i < count; i++) { - jerry_value_t ret = jerry_promise_resolve( - MODULE_FRAME_PENDING[i], jerry_undefined() - ); - jerry_value_free(ret); - jerry_value_free(MODULE_FRAME_PENDING[i]); - } -} - -void moduleFrameInit(void) { - MODULE_FRAME_PENDING_COUNT = 0; - moduleBaseDefineGlobalMethod("frame", moduleFrameFrame); -} - -void moduleFrameDispose(void) { - for(uint32_t i = 0; i < MODULE_FRAME_PENDING_COUNT; i++) { - jerry_value_free(MODULE_FRAME_PENDING[i]); - } - MODULE_FRAME_PENDING_COUNT = 0; -} diff --git a/src/dusk/script/module/engine/moduleframe.h b/src/dusk/script/module/engine/moduleframe.h deleted file mode 100644 index 136d6e32..00000000 --- a/src/dusk/script/module/engine/moduleframe.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" - -/** Maximum number of concurrent frame() awaits. */ -#define MODULE_FRAME_PENDING_MAX 64 - -extern jerry_value_t MODULE_FRAME_PENDING[MODULE_FRAME_PENDING_MAX]; -extern uint32_t MODULE_FRAME_PENDING_COUNT; - -/** - * frame() - returns a Promise that resolves at the start of the next frame. - * Used as `await frame()` inside an async script loop. - */ -moduleBaseFunction(moduleFrameFrame); - -/** - * Resolves all pending frame() promises. Must be called once per frame before - * jerry_run_jobs() so that awaiting scripts resume in the same tick. - */ -void moduleFrameFlush(void); - -/** - * Initializes the frame module and registers the global frame() function. - */ -void moduleFrameInit(void); - -/** - * Disposes the frame module, releasing any unresolved pending promises. - */ -void moduleFrameDispose(void); diff --git a/src/dusk/script/module/engine/moduletimeout.c b/src/dusk/script/module/engine/moduletimeout.c deleted file mode 100644 index 7417e408..00000000 --- a/src/dusk/script/module/engine/moduletimeout.c +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletimeout.h" - -moduletimeoutentry_t MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_MAX]; -uint32_t MODULE_TIMEOUT_PENDING_COUNT = 0; - -moduleBaseFunction(moduleTimeoutTimeout) { - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - - if(MODULE_TIMEOUT_PENDING_COUNT >= MODULE_TIMEOUT_PENDING_MAX) { - return moduleBaseThrow("Too many pending timeout() calls"); - } - - float_t ms = moduleBaseArgFloat(0); - jerry_value_t promise = jerry_promise(); - MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_COUNT].promise = - jerry_value_copy(promise); - MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_COUNT].targetTime = - TIME.time + ms / 1000.0f; - MODULE_TIMEOUT_PENDING_COUNT++; - return promise; -} - -void moduleTimeoutFlush(void) { - uint32_t i = 0; - while(i < MODULE_TIMEOUT_PENDING_COUNT) { - if(TIME.time >= MODULE_TIMEOUT_PENDING[i].targetTime) { - jerry_value_t ret = jerry_promise_resolve( - MODULE_TIMEOUT_PENDING[i].promise, jerry_undefined() - ); - jerry_value_free(ret); - jerry_value_free(MODULE_TIMEOUT_PENDING[i].promise); - MODULE_TIMEOUT_PENDING_COUNT--; - if(i < MODULE_TIMEOUT_PENDING_COUNT) { - MODULE_TIMEOUT_PENDING[i] = - MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_COUNT]; - } - } else { - i++; - } - } -} - -void moduleTimeoutInit(void) { - MODULE_TIMEOUT_PENDING_COUNT = 0; - moduleBaseDefineGlobalMethod("timeout", moduleTimeoutTimeout); -} - -void moduleTimeoutDispose(void) { - for(uint32_t i = 0; i < MODULE_TIMEOUT_PENDING_COUNT; i++) { - jerry_value_free(MODULE_TIMEOUT_PENDING[i].promise); - } - MODULE_TIMEOUT_PENDING_COUNT = 0; -} diff --git a/src/dusk/script/module/engine/moduletimeout.h b/src/dusk/script/module/engine/moduletimeout.h deleted file mode 100644 index 09eaf940..00000000 --- a/src/dusk/script/module/engine/moduletimeout.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "time/time.h" - -/** Maximum number of concurrent timeout() awaits. */ -#define MODULE_TIMEOUT_PENDING_MAX 64 - -/** Entry tracking one pending timeout() promise. */ -typedef struct { - jerry_value_t promise; - float_t targetTime; -} moduletimeoutentry_t; - -extern moduletimeoutentry_t MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_MAX]; -extern uint32_t MODULE_TIMEOUT_PENDING_COUNT; - -/** - * timeout(ms) - returns a Promise that resolves after at least `ms` - * milliseconds have elapsed. Used as `await timeout(500)`. - * - * @param args[0] Delay in milliseconds (number). - */ -moduleBaseFunction(moduleTimeoutTimeout); - -/** - * Resolves any pending timeout() promises whose target time has passed. - * Must be called once per frame before jerry_run_jobs(). - */ -void moduleTimeoutFlush(void); - -/** - * Initializes the timeout module and registers the global timeout() function. - */ -void moduleTimeoutInit(void); - -/** - * Disposes the timeout module, releasing any unresolved pending promises. - */ -void moduleTimeoutDispose(void); diff --git a/src/dusk/script/module/entity/component/CMakeLists.txt b/src/dusk/script/module/entity/component/CMakeLists.txt deleted file mode 100644 index 18ba1045..00000000 --- a/src/dusk/script/module/entity/component/CMakeLists.txt +++ /dev/null @@ -1,14 +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 - modulecomponentlist.c -) - -# Subdirs -add_subdirectory(display) -add_subdirectory(physics) -add_subdirectory(trigger) diff --git a/src/dusk/script/module/entity/component/display/CMakeLists.txt b/src/dusk/script/module/entity/component/display/CMakeLists.txt deleted file mode 100644 index 76c71939..00000000 --- a/src/dusk/script/module/entity/component/display/CMakeLists.txt +++ /dev/null @@ -1,11 +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 - modulecamera.c - moduleposition.c - modulerenderable.c -) diff --git a/src/dusk/script/module/entity/component/display/modulecamera.c b/src/dusk/script/module/entity/component/display/modulecamera.c deleted file mode 100644 index df143525..00000000 --- a/src/dusk/script/module/entity/component/display/modulecamera.c +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecamera.h" - -scriptproto_t MODULE_CAMERA_PROTO; - -jscomponent_t *moduleCameraSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_CAMERA_PROTO, callInfo->this_value - ); -} - -entitycamera_t *moduleCameraData(const jscomponent_t *c) { - return (entitycamera_t *)componentGetData( - c->entityId, c->componentId, COMPONENT_TYPE_CAMERA - ); -} - -moduleBaseFunction(moduleCameraCtor) { - return moduleBaseThrow("Camera cannot be instantiated with new"); -} - -moduleBaseFunction(moduleCameraGetEntity) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(moduleCameraGetId) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(moduleCameraGetFov) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - return jerry_number((double)cam->perspective.fov); -} - -moduleBaseFunction(moduleCameraSetFov) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - cam->perspective.fov = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleCameraGetNearClip) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - return jerry_number((double)cam->nearClip); -} - -moduleBaseFunction(moduleCameraSetNearClip) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - cam->nearClip = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleCameraGetFarClip) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - return jerry_number((double)cam->farClip); -} - -moduleBaseFunction(moduleCameraSetFarClip) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - cam->farClip = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleCameraGetProjType) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - return jerry_number((double)cam->projType); -} - -moduleBaseFunction(moduleCameraSetProjType) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - cam->projType = (entitycameraprojectiontype_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleCameraToString) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_string_sz("Camera:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Camera(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void moduleCameraInit(void) { - scriptProtoInit( - &MODULE_CAMERA_PROTO, "Camera", - sizeof(jscomponent_t), moduleCameraCtor - ); - - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "entity", moduleCameraGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "id", moduleCameraGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "fov", moduleCameraGetFov, moduleCameraSetFov - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "nearClip", - moduleCameraGetNearClip, moduleCameraSetNearClip - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "farClip", - moduleCameraGetFarClip, moduleCameraSetFarClip - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "projType", - moduleCameraGetProjType, moduleCameraSetProjType - ); - scriptProtoDefineToString(&MODULE_CAMERA_PROTO, moduleCameraToString); - - jerry_value_t ctor = MODULE_CAMERA_PROTO.constructor; - struct { const char_t *name; int val; } projtypes[] = { - { "PERSPECTIVE", ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE }, - { - "PERSPECTIVE_FLIPPED", - ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED - }, - { "ORTHOGRAPHIC", ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC }, - }; - for(int i = 0; i < 3; i++) { - jerry_value_t k = jerry_string_sz(projtypes[i].name); - jerry_value_t v = jerry_number((double)projtypes[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleCameraDispose(void) { - scriptProtoDispose(&MODULE_CAMERA_PROTO); -} diff --git a/src/dusk/script/module/entity/component/display/modulecamera.h b/src/dusk/script/module/entity/component/display/modulecamera.h deleted file mode 100644 index b3f4af5e..00000000 --- a/src/dusk/script/module/entity/component/display/modulecamera.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/entity/modulecomponent.h" -#include "entity/component/display/entitycamera.h" - -extern scriptproto_t MODULE_CAMERA_PROTO; - -/** Camera() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleCameraCtor); - -/** @return Entity ID that owns this camera component. */ -moduleBaseFunction(moduleCameraGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(moduleCameraGetId); - -/** @return Field of view in degrees. */ -moduleBaseFunction(moduleCameraGetFov); - -/** Sets field of view. @param args[0] FOV in degrees. */ -moduleBaseFunction(moduleCameraSetFov); - -/** @return Near clip plane distance. */ -moduleBaseFunction(moduleCameraGetNearClip); - -/** Sets near clip plane. @param args[0] Near clip distance. */ -moduleBaseFunction(moduleCameraSetNearClip); - -/** @return Far clip plane distance. */ -moduleBaseFunction(moduleCameraGetFarClip); - -/** Sets far clip plane. @param args[0] Far clip distance. */ -moduleBaseFunction(moduleCameraSetFarClip); - -/** @return Projection type constant (Camera.PERSPECTIVE etc.). */ -moduleBaseFunction(moduleCameraGetProjType); - -/** Sets projection type. @param args[0] entitycameraprojectiontype_t value. */ -moduleBaseFunction(moduleCameraSetProjType); - -/** @return "Camera(id)" string. */ -moduleBaseFunction(moduleCameraToString); - -/** - * Initializes the Camera module and registers the global Camera class with - * fov/nearClip/farClip/projType properties and PERSPECTIVE/ORTHOGRAPHIC - * constants. - */ -void moduleCameraInit(void); - -/** - * Disposes the Camera module. - */ -void moduleCameraDispose(void); diff --git a/src/dusk/script/module/entity/component/display/moduleposition.c b/src/dusk/script/module/entity/component/display/moduleposition.c deleted file mode 100644 index ccdfb1a0..00000000 --- a/src/dusk/script/module/entity/component/display/moduleposition.c +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleposition.h" - -scriptproto_t MODULE_POSITION_PROTO; - -jscomponent_t *modulePositionSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_POSITION_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(modulePositionCtor) { - return moduleBaseThrow("Position cannot be instantiated with new"); -} - -moduleBaseFunction(modulePositionGetEntity) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(modulePositionGetId) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(modulePositionGetLocalPos) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetLocalPosition(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetLocalPos) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.localPosition: expected Vec3"); - entityPositionSetLocalPosition(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetWorldPos) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetWorldPosition(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetWorldPos) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.worldPosition: expected Vec3"); - entityPositionSetWorldPosition(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetLocalRot) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetLocalRotation(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetLocalRot) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.localRotation: expected Vec3"); - entityPositionSetLocalRotation(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetWorldRot) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetWorldRotation(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetWorldRot) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.worldRotation: expected Vec3"); - entityPositionSetWorldRotation(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetLocalScale) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetLocalScale(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetLocalScale) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.localScale: expected Vec3"); - entityPositionSetLocalScale(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetWorldScale) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetWorldScale(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetWorldScale) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.worldScale: expected Vec3"); - entityPositionSetWorldScale(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionLookAt) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - moduleBaseRequireArgs(1); - float_t *target = moduleVec3From(args[0]); - if(!target) return moduleBaseThrow("Position.lookAt: expected Vec3 target"); - - vec3 eye; - entityPositionGetLocalPosition(c->entityId, c->componentId, eye); - - vec3 up = { 0.0f, 1.0f, 0.0f }; - if(argc >= 2) { - float_t *upArg = moduleVec3From(args[1]); - if(upArg) glm_vec3_copy(upArg, up); - } - - entityPositionLookAt(c->entityId, c->componentId, eye, target, up); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionSetParent) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - if(argc == 0 || - jerry_value_is_null(args[0]) || - jerry_value_is_undefined(args[0])) { - entityPositionSetParent( - c->entityId, c->componentId, - ENTITY_ID_INVALID, COMPONENT_ID_INVALID - ); - return jerry_undefined(); - } - jscomponent_t *parent = (jscomponent_t *)scriptProtoGetValue( - &MODULE_POSITION_PROTO, args[0] - ); - if(!parent) { - return moduleBaseThrow( - "Position.setParent: expected Position or null" - ); - } - entityPositionSetParent( - c->entityId, c->componentId, - parent->entityId, parent->componentId - ); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionToString) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_string_sz("Position:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Position(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void modulePositionInit(void) { - scriptProtoInit( - &MODULE_POSITION_PROTO, "Position", - sizeof(jscomponent_t), modulePositionCtor - ); - - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "entity", modulePositionGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "id", modulePositionGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "localPosition", - modulePositionGetLocalPos, modulePositionSetLocalPos - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "worldPosition", - modulePositionGetWorldPos, modulePositionSetWorldPos - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "localRotation", - modulePositionGetLocalRot, modulePositionSetLocalRot - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "worldRotation", - modulePositionGetWorldRot, modulePositionSetWorldRot - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "localScale", - modulePositionGetLocalScale, modulePositionSetLocalScale - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "worldScale", - modulePositionGetWorldScale, modulePositionSetWorldScale - ); - scriptProtoDefineFunc( - &MODULE_POSITION_PROTO, "lookAt", modulePositionLookAt - ); - scriptProtoDefineFunc( - &MODULE_POSITION_PROTO, "setParent", modulePositionSetParent - ); - scriptProtoDefineToString(&MODULE_POSITION_PROTO, modulePositionToString); -} - -void modulePositionDispose(void) { - scriptProtoDispose(&MODULE_POSITION_PROTO); -} diff --git a/src/dusk/script/module/entity/component/display/moduleposition.h b/src/dusk/script/module/entity/component/display/moduleposition.h deleted file mode 100644 index d24bcdcf..00000000 --- a/src/dusk/script/module/entity/component/display/moduleposition.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/math/modulevec3.h" -#include "script/module/entity/modulecomponent.h" -#include "entity/component/display/entityposition.h" - -extern scriptproto_t MODULE_POSITION_PROTO; - -/** Position() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(modulePositionCtor); - -/** @return Entity ID that owns this position component. */ -moduleBaseFunction(modulePositionGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(modulePositionGetId); - -/** @return Local position as a Vec3. */ -moduleBaseFunction(modulePositionGetLocalPos); - -/** Sets local position. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetLocalPos); - -/** @return World position as a Vec3. */ -moduleBaseFunction(modulePositionGetWorldPos); - -/** Sets world position. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetWorldPos); - -/** @return Local rotation as a Vec3 (Euler angles). */ -moduleBaseFunction(modulePositionGetLocalRot); - -/** Sets local rotation. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetLocalRot); - -/** @return World rotation as a Vec3 (Euler angles). */ -moduleBaseFunction(modulePositionGetWorldRot); - -/** Sets world rotation. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetWorldRot); - -/** @return Local scale as a Vec3. */ -moduleBaseFunction(modulePositionGetLocalScale); - -/** Sets local scale. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetLocalScale); - -/** @return World scale as a Vec3. */ -moduleBaseFunction(modulePositionGetWorldScale); - -/** Sets world scale. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetWorldScale); - -/** - * lookAt(target, up?) - orients the component toward a target point. - * @param args[0] Vec3 target position. - * @param args[1] Optional Vec3 up vector (defaults to world up). - */ -moduleBaseFunction(modulePositionLookAt); - -/** - * setParent(position?) - parents this component to another Position, or - * unparents when called with null/undefined. - * @param args[0] Position component or null/undefined. - */ -moduleBaseFunction(modulePositionSetParent); - -/** @return "Position(id)" string. */ -moduleBaseFunction(modulePositionToString); - -/** - * Initializes the Position module and registers the global Position class with - * localPosition/worldPosition/localRotation/worldRotation/localScale/worldScale - * properties and lookAt/setParent methods. - */ -void modulePositionInit(void); - -/** - * Disposes the Position module. - */ -void modulePositionDispose(void); diff --git a/src/dusk/script/module/entity/component/display/modulerenderable.c b/src/dusk/script/module/entity/component/display/modulerenderable.c deleted file mode 100644 index a0e23a8c..00000000 --- a/src/dusk/script/module/entity/component/display/modulerenderable.c +++ /dev/null @@ -1,278 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulerenderable.h" - -scriptproto_t MODULE_RENDERABLE_PROTO; - -jscomponent_t *moduleRenderableSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_RENDERABLE_PROTO, callInfo->this_value - ); -} - -entityrenderable_t *moduleRenderableData(const jscomponent_t *c) { - return (entityrenderable_t *)componentGetData( - c->entityId, c->componentId, COMPONENT_TYPE_RENDERABLE - ); -} - -float_t moduleRenderableArrayFloat( - const jerry_value_t arr, - const uint32_t idx, - const float_t def -) { - if(idx >= jerry_array_length(arr)) return def; - jerry_value_t v = jerry_object_get_index(arr, idx); - float_t f = jerry_value_is_number(v) - ? (float_t)jerry_value_as_number(v) : def; - jerry_value_free(v); - return f; -} - -moduleBaseFunction(moduleRenderableCtor) { - return moduleBaseThrow("Renderable cannot be instantiated with new"); -} - -moduleBaseFunction(moduleRenderableGetEntity) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(moduleRenderableGetId) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(moduleRenderableGetType) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - return jerry_number((double)r->type); -} - -moduleBaseFunction(moduleRenderableSetType) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityRenderableSetType( - c->entityId, c->componentId, - (entityrenderabletype_t)moduleBaseArgInt(0) - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableGetPriority) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - return jerry_number((double)r->priority); -} - -moduleBaseFunction(moduleRenderableSetPriority) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityRenderableSetPriority( - c->entityId, c->componentId, - (int8_t)moduleBaseArgInt(0) - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableGetColor) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - return moduleColorPush(r->data.material.material.unlit.color); -} - -moduleBaseFunction(moduleRenderableSetColor) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - color_t *col = moduleColorFrom(args[0]); - if(!col) return moduleBaseThrow("Renderable.color: expected Color"); - r->data.material.material.unlit.color = *col; - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableGetTexture) { - jerry_value_t key = jerry_string_sz("_tex"); - jerry_value_t val = jerry_object_get(callInfo->this_value, key); - jerry_value_free(key); - return val; -} - -moduleBaseFunction(moduleRenderableSetTexture) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - jstexture_t *tex = (jstexture_t *)scriptProtoGetValue( - &MODULE_TEXTURE_PROTO, args[0] - ); - if(!tex || !tex->entry) { - return moduleBaseThrow("Renderable.texture: expected Texture"); - } - r->type = ENTITY_RENDERABLE_TYPE_SPRITEBATCH; - r->data.spritebatch.texture = &tex->entry->data.texture; - jerry_value_t pinKey = jerry_string_sz("_tex"); - jerry_object_set(callInfo->this_value, pinKey, args[0]); - jerry_value_free(pinKey); - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableGetSprites) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - const entityrenderablespritebatch_t *sb = &r->data.spritebatch; - - jerry_value_t arr = jerry_array((uint32_t)sb->spriteCount); - for(uint32_t i = 0; i < (uint32_t)sb->spriteCount; i++) { - const spritebatchsprite_t *s = &sb->sprites[i]; - float_t vals[10] = { - s->min[0], s->min[1], s->min[2], - s->max[0], s->max[1], s->max[2], - s->uvMin[0], s->uvMin[1], - s->uvMax[0], s->uvMax[1], - }; - jerry_value_t sprite = jerry_array(10); - for(uint32_t j = 0; j < 10; j++) { - jerry_value_t num = jerry_number((double)vals[j]); - jerry_object_set_index(sprite, j, num); - jerry_value_free(num); - } - jerry_object_set_index(arr, i, sprite); - jerry_value_free(sprite); - } - return arr; -} - -moduleBaseFunction(moduleRenderableSetSprites) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - if(!jerry_value_is_array(args[0])) { - return moduleBaseThrow("Renderable.sprites: expected Array"); - } - entityrenderablespritebatch_t *sb = &r->data.spritebatch; - uint32_t count = jerry_array_length(args[0]); - if(count > ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX) { - return moduleBaseThrow("Renderable.sprites: exceeds sprite capacity"); - } - sb->spriteCount = 0; - for(uint32_t i = 0; i < count; i++) { - jerry_value_t elem = jerry_object_get_index(args[0], i); - if(!jerry_value_is_array(elem)) { - jerry_value_free(elem); - return moduleBaseThrow( - "Renderable.sprites: each element must be an Array" - ); - } - spritebatchsprite_t s; - if(jerry_array_length(elem) >= 10) { - s.min[0] = moduleRenderableArrayFloat(elem, 0, 0.0f); - s.min[1] = moduleRenderableArrayFloat(elem, 1, 0.0f); - s.min[2] = moduleRenderableArrayFloat(elem, 2, 0.0f); - s.max[0] = moduleRenderableArrayFloat(elem, 3, 0.0f); - s.max[1] = moduleRenderableArrayFloat(elem, 4, 0.0f); - s.max[2] = moduleRenderableArrayFloat(elem, 5, 0.0f); - s.uvMin[0] = moduleRenderableArrayFloat(elem, 6, 0.0f); - s.uvMin[1] = moduleRenderableArrayFloat(elem, 7, 0.0f); - s.uvMax[0] = moduleRenderableArrayFloat(elem, 8, 1.0f); - s.uvMax[1] = moduleRenderableArrayFloat(elem, 9, 1.0f); - } else { - s.min[0] = moduleRenderableArrayFloat(elem, 0, 0.0f); - s.min[1] = moduleRenderableArrayFloat(elem, 1, 0.0f); - s.min[2] = 0.0f; - s.max[0] = moduleRenderableArrayFloat(elem, 2, 0.0f); - s.max[1] = moduleRenderableArrayFloat(elem, 3, 0.0f); - s.max[2] = 0.0f; - s.uvMin[0] = moduleRenderableArrayFloat(elem, 4, 0.0f); - s.uvMin[1] = moduleRenderableArrayFloat(elem, 5, 0.0f); - s.uvMax[0] = moduleRenderableArrayFloat(elem, 6, 1.0f); - s.uvMax[1] = moduleRenderableArrayFloat(elem, 7, 1.0f); - } - jerry_value_free(elem); - sb->sprites[sb->spriteCount++] = s; - } - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableToString) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_string_sz("Renderable:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Renderable(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void moduleRenderableInit(void) { - scriptProtoInit( - &MODULE_RENDERABLE_PROTO, "Renderable", - sizeof(jscomponent_t), moduleRenderableCtor - ); - - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "entity", moduleRenderableGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "id", moduleRenderableGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "type", - moduleRenderableGetType, moduleRenderableSetType - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "priority", - moduleRenderableGetPriority, moduleRenderableSetPriority - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "color", - moduleRenderableGetColor, moduleRenderableSetColor - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "texture", - moduleRenderableGetTexture, moduleRenderableSetTexture - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "sprites", - moduleRenderableGetSprites, moduleRenderableSetSprites - ); - scriptProtoDefineToString(&MODULE_RENDERABLE_PROTO, moduleRenderableToString); - - jerry_value_t ctor = MODULE_RENDERABLE_PROTO.constructor; - struct { const char_t *name; int val; } types[] = { - { "SHADER_MATERIAL", ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL }, - { "SPRITEBATCH", ENTITY_RENDERABLE_TYPE_SPRITEBATCH }, - { "CUSTOM", ENTITY_RENDERABLE_TYPE_CUSTOM }, - }; - for(int i = 0; i < 3; i++) { - jerry_value_t k = jerry_string_sz(types[i].name); - jerry_value_t v = jerry_number((double)types[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleRenderableDispose(void) { - scriptProtoDispose(&MODULE_RENDERABLE_PROTO); -} diff --git a/src/dusk/script/module/entity/component/display/modulerenderable.h b/src/dusk/script/module/entity/component/display/modulerenderable.h deleted file mode 100644 index 39c1ef64..00000000 --- a/src/dusk/script/module/entity/component/display/modulerenderable.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/module/display/modulecolor.h" -#include "script/module/display/moduletexture.h" -#include "script/module/entity/modulecomponent.h" -#include "script/scriptproto.h" -#include "entity/component/display/entityrenderable.h" - -extern scriptproto_t MODULE_RENDERABLE_PROTO; - -/** Renderable() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleRenderableCtor); - -/** @return Entity ID that owns this renderable component. */ -moduleBaseFunction(moduleRenderableGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(moduleRenderableGetId); - -/** @return Render type constant (Renderable.SPRITEBATCH etc.). */ -moduleBaseFunction(moduleRenderableGetType); - -/** Sets render type. @param args[0] entityrenderabletype_t value. */ -moduleBaseFunction(moduleRenderableSetType); - -/** @return Render priority (signed integer). */ -moduleBaseFunction(moduleRenderableGetPriority); - -/** Sets render priority. @param args[0] Priority integer. */ -moduleBaseFunction(moduleRenderableSetPriority); - -/** @return Material color as a Color object. */ -moduleBaseFunction(moduleRenderableGetColor); - -/** Sets material color. @param args[0] Color object. */ -moduleBaseFunction(moduleRenderableSetColor); - -/** @return The pinned Texture JS instance, or undefined if none set. */ -moduleBaseFunction(moduleRenderableGetTexture); - -/** - * Sets the texture for SPRITEBATCH rendering. Also switches the render type to - * SPRITEBATCH and pins the JS Texture object to prevent GC. - * @param args[0] Texture JS object. - */ -moduleBaseFunction(moduleRenderableSetTexture); - -/** - * @return JS array of sprite sub-arrays, each with 10 numbers: - * [x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]. - */ -moduleBaseFunction(moduleRenderableGetSprites); - -/** - * Sets sprite data. Accepts an array of sub-arrays - 10 elements (3D) or - * 8 elements (2D, z defaults to 0). - * @param args[0] Array of sprite sub-arrays. - */ -moduleBaseFunction(moduleRenderableSetSprites); - -/** @return "Renderable(id)" string. */ -moduleBaseFunction(moduleRenderableToString); - -/** - * Initializes the Renderable module and registers the global Renderable class - * with type/priority/color/texture/sprites properties and SPRITEBATCH/ - * SHADER_MATERIAL/CUSTOM constants. - */ -void moduleRenderableInit(void); - -/** - * Disposes the Renderable module. - */ -void moduleRenderableDispose(void); diff --git a/src/dusk/script/module/entity/component/modulecomponentlist.c b/src/dusk/script/module/entity/component/modulecomponentlist.c deleted file mode 100644 index 472b3605..00000000 --- a/src/dusk/script/module/entity/component/modulecomponentlist.c +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecomponentlist.h" - -jerry_value_t moduleComponentListCreateInstance( - const componenttype_t type, - const jscomponent_t *comp -) { - switch(type) { - case COMPONENT_TYPE_CAMERA: - return scriptProtoCreateValue(&MODULE_CAMERA_PROTO, comp); - case COMPONENT_TYPE_PHYSICS: - return scriptProtoCreateValue(&MODULE_PHYSICS_PROTO, comp); - case COMPONENT_TYPE_POSITION: - return scriptProtoCreateValue(&MODULE_POSITION_PROTO, comp); - case COMPONENT_TYPE_RENDERABLE: - return scriptProtoCreateValue(&MODULE_RENDERABLE_PROTO, comp); - case COMPONENT_TYPE_TRIGGER: - return scriptProtoCreateValue(&MODULE_TRIGGER_PROTO, comp); - default: - return scriptProtoCreateValue(&MODULE_COMPONENT_PROTO, comp); - } -} - -void moduleComponentListInit(void) { - moduleCameraInit(); - modulePhysicsInit(); - modulePositionInit(); - moduleRenderableInit(); - moduleTriggerInit(); -} - -void moduleComponentListDispose(void) { - moduleTriggerDispose(); - moduleRenderableDispose(); - modulePositionDispose(); - modulePhysicsDispose(); - moduleCameraDispose(); -} diff --git a/src/dusk/script/module/entity/component/modulecomponentlist.h b/src/dusk/script/module/entity/component/modulecomponentlist.h deleted file mode 100644 index 0399ebb6..00000000 --- a/src/dusk/script/module/entity/component/modulecomponentlist.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/entity/modulecomponent.h" -#include "display/modulecamera.h" -#include "display/moduleposition.h" -#include "display/modulerenderable.h" -#include "physics/modulephysics.h" -#include "trigger/moduletrigger.h" - -/** - * Returns a typed JS instance for a newly-added component. Falls back to the - * generic Component proto for types that have no specific module yet. - * - * @param type Component type constant. - * @param comp Initialized jscomponent_t with entityId and componentId. - * @return A jerry_value_t JS object of the appropriate subtype. - */ -jerry_value_t moduleComponentListCreateInstance( - const componenttype_t type, - const jscomponent_t *comp -); - -/** - * Initializes all component sub-modules (camera, physics, position, renderable, - * trigger). - */ -void moduleComponentListInit(void); - -/** - * Disposes all component sub-modules in reverse init order. - */ -void moduleComponentListDispose(void); diff --git a/src/dusk/script/module/entity/component/physics/CMakeLists.txt b/src/dusk/script/module/entity/component/physics/CMakeLists.txt deleted file mode 100644 index 4bb31417..00000000 --- a/src/dusk/script/module/entity/component/physics/CMakeLists.txt +++ /dev/null @@ -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 - modulephysics.c -) diff --git a/src/dusk/script/module/entity/component/physics/modulephysics.c b/src/dusk/script/module/entity/component/physics/modulephysics.c deleted file mode 100644 index ebd20035..00000000 --- a/src/dusk/script/module/entity/component/physics/modulephysics.c +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulephysics.h" -#include "util/memory.h" - -scriptproto_t MODULE_PHYSICS_PROTO; - -jscomponent_t *modulePhysicsSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_PHYSICS_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(modulePhysicsCtor) { - return moduleBaseThrow("Physics cannot be instantiated with new"); -} - -moduleBaseFunction(modulePhysicsGetEntity) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(modulePhysicsGetId) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(modulePhysicsGetBodyType) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number( - (double)entityPhysicsGetBodyType(c->entityId, c->componentId) - ); -} - -moduleBaseFunction(modulePhysicsSetBodyType) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - entityPhysicsSetBodyType( - c->entityId, c->componentId, - (physicsbodytype_t)moduleBaseArgInt(0) - ); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsGetShape) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number( - (double)entityPhysicsGetShape( - c->entityId, c->componentId - ).type - ); -} - -moduleBaseFunction(modulePhysicsSetShape) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - physicsshape_t shape; - memoryZero(&shape, sizeof(physicsshape_t)); - shape.type = (physicshapetype_t)moduleBaseArgInt(0); - switch(shape.type) { - case PHYSICS_SHAPE_CUBE: - shape.data.cube.halfExtents[0] = 0.5f; - shape.data.cube.halfExtents[1] = 0.5f; - shape.data.cube.halfExtents[2] = 0.5f; - break; - case PHYSICS_SHAPE_SPHERE: - shape.data.sphere.radius = 0.5f; - break; - case PHYSICS_SHAPE_CAPSULE: - shape.data.capsule.radius = 0.5f; - shape.data.capsule.halfHeight = 0.5f; - break; - case PHYSICS_SHAPE_PLANE: - shape.data.plane.normal[1] = 1.0f; - shape.data.plane.distance = 0.0f; - break; - default: - break; - } - entityPhysicsSetShape(c->entityId, c->componentId, shape); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsGetVelocity) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPhysicsGetVelocity(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePhysicsSetVelocity) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Physics.velocity: expected Vec3"); - entityPhysicsSetVelocity(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsGetGravityScale) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - entityphysics_t *p = entityPhysicsGet(c->entityId, c->componentId); - if(!p) return jerry_undefined(); - return jerry_number((double)p->gravityScale); -} - -moduleBaseFunction(modulePhysicsSetGravityScale) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - entityphysics_t *p = entityPhysicsGet(c->entityId, c->componentId); - if(!p) return jerry_undefined(); - p->gravityScale = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsGetOnGround) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_boolean(entityPhysicsIsOnGround(c->entityId, c->componentId)); -} - -moduleBaseFunction(modulePhysicsApplyImpulse) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Physics.applyImpulse: expected Vec3"); - entityPhysicsApplyImpulse(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsToString) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_string_sz("Physics:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Physics(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void modulePhysicsInit(void) { - scriptProtoInit( - &MODULE_PHYSICS_PROTO, "Physics", - sizeof(jscomponent_t), modulePhysicsCtor - ); - - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "entity", modulePhysicsGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "id", modulePhysicsGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "bodyType", - modulePhysicsGetBodyType, modulePhysicsSetBodyType - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "shape", - modulePhysicsGetShape, modulePhysicsSetShape - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "velocity", - modulePhysicsGetVelocity, modulePhysicsSetVelocity - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "gravityScale", - modulePhysicsGetGravityScale, modulePhysicsSetGravityScale - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "onGround", modulePhysicsGetOnGround, NULL - ); - scriptProtoDefineFunc( - &MODULE_PHYSICS_PROTO, "applyImpulse", modulePhysicsApplyImpulse - ); - scriptProtoDefineToString(&MODULE_PHYSICS_PROTO, modulePhysicsToString); - - jerry_value_t ctor = MODULE_PHYSICS_PROTO.constructor; - struct { const char_t *name; int val; } bodyTypes[] = { - { "STATIC", PHYSICS_BODY_STATIC }, - { "DYNAMIC", PHYSICS_BODY_DYNAMIC }, - { "KINEMATIC", PHYSICS_BODY_KINEMATIC }, - }; - for(int i = 0; i < 3; i++) { - jerry_value_t k = jerry_string_sz(bodyTypes[i].name); - jerry_value_t v = jerry_number((double)bodyTypes[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } - - struct { const char_t *name; int val; } shapes[] = { - { "SHAPE_CUBE", PHYSICS_SHAPE_CUBE }, - { "SHAPE_SPHERE", PHYSICS_SHAPE_SPHERE }, - { "SHAPE_CAPSULE", PHYSICS_SHAPE_CAPSULE }, - { "SHAPE_PLANE", PHYSICS_SHAPE_PLANE }, - }; - for(int i = 0; i < 4; i++) { - jerry_value_t k = jerry_string_sz(shapes[i].name); - jerry_value_t v = jerry_number((double)shapes[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void modulePhysicsDispose(void) { - scriptProtoDispose(&MODULE_PHYSICS_PROTO); -} diff --git a/src/dusk/script/module/entity/component/physics/modulephysics.h b/src/dusk/script/module/entity/component/physics/modulephysics.h deleted file mode 100644 index c4fbcafa..00000000 --- a/src/dusk/script/module/entity/component/physics/modulephysics.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/math/modulevec3.h" -#include "script/module/entity/modulecomponent.h" -#include "entity/component/physics/entityphysics.h" - -extern scriptproto_t MODULE_PHYSICS_PROTO; - -/** Physics() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(modulePhysicsCtor); - -/** @return Entity ID that owns this physics component. */ -moduleBaseFunction(modulePhysicsGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(modulePhysicsGetId); - -/** @return Body type constant (Physics.STATIC, .DYNAMIC, .KINEMATIC). */ -moduleBaseFunction(modulePhysicsGetBodyType); - -/** Sets body type. @param args[0] physicsbodytype_t value. */ -moduleBaseFunction(modulePhysicsSetBodyType); - -/** @return Collision shape type constant (Physics.SHAPE_*). */ -moduleBaseFunction(modulePhysicsGetShape); - -/** Sets collision shape type. @param args[0] physicshapetype_t value. */ -moduleBaseFunction(modulePhysicsSetShape); - -/** @return Linear velocity as a Vec3. */ -moduleBaseFunction(modulePhysicsGetVelocity); - -/** Sets linear velocity. @param args[0] Vec3. */ -moduleBaseFunction(modulePhysicsSetVelocity); - -/** @return Gravity scale multiplier (float). */ -moduleBaseFunction(modulePhysicsGetGravityScale); - -/** Sets gravity scale. @param args[0] Float multiplier. */ -moduleBaseFunction(modulePhysicsSetGravityScale); - -/** @return True when the body is resting on a surface. */ -moduleBaseFunction(modulePhysicsGetOnGround); - -/** - * applyImpulse(impulse) - applies an instantaneous force to the body. - * @param args[0] Vec3 impulse vector. - */ -moduleBaseFunction(modulePhysicsApplyImpulse); - -/** @return "Physics(id)" string. */ -moduleBaseFunction(modulePhysicsToString); - -/** - * Initializes the Physics module and registers the global Physics class with - * bodyType/shape/velocity/gravityScale/onGround properties, applyImpulse - * method, and STATIC/DYNAMIC/KINEMATIC and SHAPE_* constants. - */ -void modulePhysicsInit(void); - -/** - * Disposes the Physics module. - */ -void modulePhysicsDispose(void); diff --git a/src/dusk/script/module/entity/component/trigger/CMakeLists.txt b/src/dusk/script/module/entity/component/trigger/CMakeLists.txt deleted file mode 100644 index 915db5ac..00000000 --- a/src/dusk/script/module/entity/component/trigger/CMakeLists.txt +++ /dev/null @@ -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 - moduletrigger.c -) diff --git a/src/dusk/script/module/entity/component/trigger/moduletrigger.c b/src/dusk/script/module/entity/component/trigger/moduletrigger.c deleted file mode 100644 index 1cf5c5b6..00000000 --- a/src/dusk/script/module/entity/component/trigger/moduletrigger.c +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletrigger.h" - -scriptproto_t MODULE_TRIGGER_PROTO; - -jscomponent_t *moduleTriggerSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_TRIGGER_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(moduleTriggerCtor) { - return moduleBaseThrow("Trigger cannot be instantiated with new"); -} - -moduleBaseFunction(moduleTriggerGetEntity) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(moduleTriggerGetId) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(moduleTriggerGetMin) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - entitytrigger_t *t = entityTriggerGet(c->entityId, c->componentId); - if(!t) return jerry_undefined(); - return moduleVec3Push(t->min); -} - -moduleBaseFunction(moduleTriggerSetMin) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Trigger.min: expected Vec3"); - entitytrigger_t *t = entityTriggerGet(c->entityId, c->componentId); - if(!t) return jerry_undefined(); - glm_vec3_copy(v, t->min); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTriggerGetMax) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - entitytrigger_t *t = entityTriggerGet(c->entityId, c->componentId); - if(!t) return jerry_undefined(); - return moduleVec3Push(t->max); -} - -moduleBaseFunction(moduleTriggerSetMax) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Trigger.max: expected Vec3"); - entitytrigger_t *t = entityTriggerGet(c->entityId, c->componentId); - if(!t) return jerry_undefined(); - glm_vec3_copy(v, t->max); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTriggerSetBounds) { - moduleBaseRequireArgs(2); - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *minV = moduleVec3From(args[0]); - float_t *maxV = moduleVec3From(args[1]); - if(!minV) return moduleBaseThrow("Trigger.setBounds: expected Vec3 for min"); - if(!maxV) return moduleBaseThrow("Trigger.setBounds: expected Vec3 for max"); - entityTriggerSetBounds(c->entityId, c->componentId, minV, maxV); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTriggerContains) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Trigger.contains: expected Vec3"); - return jerry_boolean(entityTriggerContains(c->entityId, c->componentId, v)); -} - -moduleBaseFunction(moduleTriggerToString) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_string_sz("Trigger:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Trigger(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void moduleTriggerInit(void) { - scriptProtoInit( - &MODULE_TRIGGER_PROTO, "Trigger", - sizeof(jscomponent_t), moduleTriggerCtor - ); - - scriptProtoDefineProp( - &MODULE_TRIGGER_PROTO, "entity", moduleTriggerGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_TRIGGER_PROTO, "id", moduleTriggerGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_TRIGGER_PROTO, "min", moduleTriggerGetMin, moduleTriggerSetMin - ); - scriptProtoDefineProp( - &MODULE_TRIGGER_PROTO, "max", moduleTriggerGetMax, moduleTriggerSetMax - ); - scriptProtoDefineFunc( - &MODULE_TRIGGER_PROTO, "setBounds", moduleTriggerSetBounds - ); - scriptProtoDefineFunc( - &MODULE_TRIGGER_PROTO, "contains", moduleTriggerContains - ); - scriptProtoDefineToString(&MODULE_TRIGGER_PROTO, moduleTriggerToString); -} - -void moduleTriggerDispose(void) { - scriptProtoDispose(&MODULE_TRIGGER_PROTO); -} diff --git a/src/dusk/script/module/entity/component/trigger/moduletrigger.h b/src/dusk/script/module/entity/component/trigger/moduletrigger.h deleted file mode 100644 index f493d83e..00000000 --- a/src/dusk/script/module/entity/component/trigger/moduletrigger.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/math/modulevec3.h" -#include "script/module/entity/modulecomponent.h" -#include "entity/component/trigger/entitytrigger.h" - -extern scriptproto_t MODULE_TRIGGER_PROTO; - -/** Trigger() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleTriggerCtor); - -/** @return Entity ID that owns this trigger component. */ -moduleBaseFunction(moduleTriggerGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(moduleTriggerGetId); - -/** @return AABB minimum corner as a Vec3. */ -moduleBaseFunction(moduleTriggerGetMin); - -/** Sets AABB minimum corner. @param args[0] Vec3. */ -moduleBaseFunction(moduleTriggerSetMin); - -/** @return AABB maximum corner as a Vec3. */ -moduleBaseFunction(moduleTriggerGetMax); - -/** Sets AABB maximum corner. @param args[0] Vec3. */ -moduleBaseFunction(moduleTriggerSetMax); - -/** - * setBounds(min, max) - sets both AABB corners at once. - * @param args[0] Vec3 minimum corner. - * @param args[1] Vec3 maximum corner. - */ -moduleBaseFunction(moduleTriggerSetBounds); - -/** - * contains(point) - tests whether a world-space point is inside the AABB. - * @param args[0] Vec3 point. - * @return true if the point is inside the trigger volume. - */ -moduleBaseFunction(moduleTriggerContains); - -/** @return "Trigger(id)" string. */ -moduleBaseFunction(moduleTriggerToString); - -/** - * Initializes the Trigger module and registers the global Trigger class with - * min/max properties and setBounds/contains methods. - */ -void moduleTriggerInit(void); - -/** - * Disposes the Trigger module. - */ -void moduleTriggerDispose(void); diff --git a/src/dusk/script/module/entity/modulecomponent.c b/src/dusk/script/module/entity/modulecomponent.c deleted file mode 100644 index 3326c949..00000000 --- a/src/dusk/script/module/entity/modulecomponent.c +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecomponent.h" - -scriptproto_t MODULE_COMPONENT_PROTO; - -moduleBaseFunction(moduleComponentCtor) { - return moduleBaseThrow("Component cannot be instantiated with new"); -} - -moduleBaseFunction(moduleComponentGetEntity) { - jscomponent_t *c = (jscomponent_t *)scriptProtoGetValue( - &MODULE_COMPONENT_PROTO, callInfo->this_value - ); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(moduleComponentGetId) { - jscomponent_t *c = (jscomponent_t *)scriptProtoGetValue( - &MODULE_COMPONENT_PROTO, callInfo->this_value - ); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(moduleComponentToString) { - jscomponent_t *c = (jscomponent_t *)scriptProtoGetValue( - &MODULE_COMPONENT_PROTO, callInfo->this_value - ); - if(!c) return jerry_string_sz("Component:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Component(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void moduleComponentInit(void) { - scriptProtoInit( - &MODULE_COMPONENT_PROTO, "Component", - sizeof(jscomponent_t), moduleComponentCtor - ); - scriptProtoDefineProp( - &MODULE_COMPONENT_PROTO, "entity", moduleComponentGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_COMPONENT_PROTO, "id", moduleComponentGetId, NULL - ); - scriptProtoDefineToString(&MODULE_COMPONENT_PROTO, moduleComponentToString); - - jerry_value_t ctor = MODULE_COMPONENT_PROTO.constructor; - #define X(enumName, type, field, init, dispose, render) { \ - jerry_value_t _k = jerry_string_sz(#enumName); \ - jerry_value_t _v = jerry_number((double)COMPONENT_TYPE_##enumName); \ - jerry_object_set(ctor, _k, _v); \ - jerry_value_free(_v); \ - jerry_value_free(_k); \ - } - #include "entity/componentlist.h" - #undef X -} - -void moduleComponentDispose(void) { - scriptProtoDispose(&MODULE_COMPONENT_PROTO); -} diff --git a/src/dusk/script/module/entity/modulecomponent.h b/src/dusk/script/module/entity/modulecomponent.h deleted file mode 100644 index 878b4a8f..00000000 --- a/src/dusk/script/module/entity/modulecomponent.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "entity/component.h" - -extern scriptproto_t MODULE_COMPONENT_PROTO; - -/** C struct wrapped by every Component JS instance. */ -typedef struct { - entityid_t entityId; - componentid_t componentId; -} jscomponent_t; - -/** Component() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleComponentCtor); - -/** @return The entity ID that owns this component. */ -moduleBaseFunction(moduleComponentGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(moduleComponentGetId); - -/** @return Component ID as a string. */ -moduleBaseFunction(moduleComponentToString); - -/** - * Initializes the Component module, registers the global Component class with - * entity/id properties and TYPE_* / INVALID constants. - */ -void moduleComponentInit(void); - -/** - * Disposes the Component module. - */ -void moduleComponentDispose(void); diff --git a/src/dusk/script/module/entity/moduleentity.c b/src/dusk/script/module/entity/moduleentity.c deleted file mode 100644 index 7897af05..00000000 --- a/src/dusk/script/module/entity/moduleentity.c +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentity.h" - -scriptproto_t MODULE_ENTITY_PROTO; - -moduleBaseFunction(moduleEntityCtor) { - return moduleBaseThrow("Entity cannot be instantiated with new"); -} - -moduleBaseFunction(moduleEntityCreate) { - entityid_t id = entityManagerAdd(); - if(id == ENTITY_ID_INVALID) { - return moduleBaseThrow("Entity.create: no entity slots available"); - } - jsentity_t ent = { .id = id }; - return scriptProtoCreateValue(&MODULE_ENTITY_PROTO, &ent); -} - -moduleBaseFunction(moduleEntityDisposeEntity) { - moduleBaseRequireArgs(1); - jsentity_t *ent = scriptProtoGetValue(&MODULE_ENTITY_PROTO, args[0]); - if(!ent) return moduleBaseThrow("Entity.dispose: expected Entity object"); - entityDispose(ent->id); - return jerry_undefined(); -} - -moduleBaseFunction(moduleEntityAdd) { - jsentity_t *ent = scriptProtoGetValue( - &MODULE_ENTITY_PROTO, callInfo->this_value - ); - if(!ent) return moduleBaseThrow("Entity.add: invalid this"); - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - - const componenttype_t type = (componenttype_t)moduleBaseArgInt(0); - if(type <= COMPONENT_TYPE_NULL || type >= COMPONENT_TYPE_COUNT) { - return moduleBaseThrow("Entity.add: invalid component type"); - } - - componentid_t cid = entityAddComponent(ent->id, type); - if(cid == COMPONENT_ID_INVALID) { - return moduleBaseThrow("Entity.add: failed to add component"); - } - - jscomponent_t comp = { .entityId = ent->id, .componentId = cid }; - return moduleComponentListCreateInstance(type, &comp); -} - -moduleBaseFunction(moduleEntityToString) { - jsentity_t *ent = scriptProtoGetValue( - &MODULE_ENTITY_PROTO, callInfo->this_value - ); - if(!ent) return jerry_string_sz("Entity:invalid"); - jerry_value_t num = jerry_number((double)ent->id); - jerry_value_t str = jerry_value_to_string(num); - jerry_value_free(num); - return str; -} - -void moduleEntityInit(void) { - moduleComponentInit(); - moduleComponentListInit(); - - scriptProtoInit( - &MODULE_ENTITY_PROTO, "Entity", - sizeof(jsentity_t), moduleEntityCtor - ); - - scriptProtoDefineStaticFunc( - &MODULE_ENTITY_PROTO, "create", moduleEntityCreate - ); - scriptProtoDefineStaticFunc( - &MODULE_ENTITY_PROTO, "dispose", moduleEntityDisposeEntity - ); - - jerry_value_t ctor = MODULE_ENTITY_PROTO.constructor; - jerry_value_t _key = jerry_string_sz("INVALID"); - jerry_value_t _val = jerry_number((double)ENTITY_ID_INVALID); - jerry_object_set(ctor, _key, _val); - jerry_value_free(_val); - jerry_value_free(_key); - - scriptProtoDefineFunc(&MODULE_ENTITY_PROTO, "add", moduleEntityAdd); - scriptProtoDefineToString(&MODULE_ENTITY_PROTO, moduleEntityToString); -} - -void moduleEntityDispose(void) { - scriptProtoDispose(&MODULE_ENTITY_PROTO); - moduleComponentListDispose(); - moduleComponentDispose(); -} diff --git a/src/dusk/script/module/entity/moduleentity.h b/src/dusk/script/module/entity/moduleentity.h deleted file mode 100644 index 9dc63ec9..00000000 --- a/src/dusk/script/module/entity/moduleentity.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/entity/modulecomponent.h" -#include "script/module/entity/component/modulecomponentlist.h" -#include "entity/entitymanager.h" - -extern scriptproto_t MODULE_ENTITY_PROTO; - -/** C struct wrapped by every Entity JS instance. */ -typedef struct { - entityid_t id; -} jsentity_t; - -/** Entity() constructor - always throws; use Entity.create() instead. */ -moduleBaseFunction(moduleEntityCtor); - -/** - * Entity.create() - allocates a new entity slot and returns an - * Entity JS object. - * @return Entity JS object. - * @throws If no entity slots are available. - */ -moduleBaseFunction(moduleEntityCreate); - -/** - * Entity.dispose(entity) - disposes an entity and all its components. - * @param args[0] Entity JS object. - */ -moduleBaseFunction(moduleEntityDisposeEntity); - -/** - * entity.add(type) - adds a component of the given type to this entity. - * @param args[0] Component type constant (e.g. COMPONENT_TYPE_POSITION). - * @return Typed component JS object (Position, Camera, etc.). - * @throws If the type is invalid or no component slots are available. - */ -moduleBaseFunction(moduleEntityAdd); - -/** @return Entity ID as a string. */ -moduleBaseFunction(moduleEntityToString); - -/** - * Initializes the Entity module and registers the global Entity class with - * create/dispose static methods, add instance method, and INVALID constant. - * Also calls moduleComponentInit() and moduleComponentListInit(). - */ -void moduleEntityInit(void); - -/** - * Disposes the Entity module and calls moduleComponentListDispose() and - * moduleComponentDispose(). - */ -void moduleEntityDispose(void); diff --git a/src/dusk/script/module/event/CMakeLists.txt b/src/dusk/script/module/event/CMakeLists.txt deleted file mode 100644 index 5bb1fd1b..00000000 --- a/src/dusk/script/module/event/CMakeLists.txt +++ /dev/null @@ -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 - moduleevent.c -) diff --git a/src/dusk/script/module/event/moduleevent.c b/src/dusk/script/module/event/moduleevent.c deleted file mode 100644 index 058bdfe4..00000000 --- a/src/dusk/script/module/event/moduleevent.c +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleevent.h" -#include "util/memory.h" -#include "assert/assert.h" - -#define MODULE_EVENT_PENDING_MAX 32 - -scriptproto_t MODULE_EVENT_PROTO; - -static scriptpromisepend_t MODULE_EVENT_PENDING[MODULE_EVENT_PENDING_MAX]; -static uint32_t MODULE_EVENT_PENDING_COUNT = 0; - -void moduleEventTrampoline0(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventTrampoline1(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventTrampoline2(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventTrampoline3(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -eventcallback_t MODULE_EVENT_TRAMPOLINES[MODULE_EVENT_MAX_SLOTS] = { - moduleEventTrampoline0, - moduleEventTrampoline1, - moduleEventTrampoline2, - moduleEventTrampoline3, -}; - -void moduleEventFree(void *ptr, jerry_object_native_info_t *info) { - jsevent_t *ev = (jsevent_t *)ptr; - if(ev) { - for(uint32_t i = 0; i < MODULE_EVENT_MAX_SLOTS; i++) { - if(jerry_value_is_function(ev->fns[i])) { - if(ev->event) { - eventUnsubscribe(ev->event, MODULE_EVENT_TRAMPOLINES[i]); - } - jerry_value_free(ev->fns[i]); - } - } - } - memoryFree(ptr); -} - -moduleBaseFunction(moduleEventOn) { - moduleBaseRequireArgs(1); - jsevent_t *ev = (jsevent_t *)scriptProtoGetValue( - &MODULE_EVENT_PROTO, callInfo->this_value - ); - if(!ev || !ev->event) { - return moduleBaseThrow("Event.on: invalid event"); - } - if(!jerry_value_is_function(args[0])) { - return moduleBaseThrow("Event.on: expected function"); - } - for(uint32_t i = 0; i < MODULE_EVENT_MAX_SLOTS; i++) { - if(!jerry_value_is_function(ev->fns[i])) { - ev->fns[i] = jerry_value_copy(args[0]); - eventSubscribe( - ev->event, - MODULE_EVENT_TRAMPOLINES[i], - (void *)(uintptr_t)ev->fns[i] - ); - return jerry_value_copy(callInfo->this_value); - } - } - return moduleBaseThrow("Event.on: no available subscriber slots"); -} - -moduleBaseFunction(moduleEventOff) { - moduleBaseRequireArgs(1); - jsevent_t *ev = (jsevent_t *)scriptProtoGetValue( - &MODULE_EVENT_PROTO, callInfo->this_value - ); - if(!ev || !ev->event) return jerry_value_copy(callInfo->this_value); - for(uint32_t i = 0; i < MODULE_EVENT_MAX_SLOTS; i++) { - if(!jerry_value_is_function(ev->fns[i])) continue; - jerry_value_t eq = jerry_binary_op( - JERRY_BIN_OP_STRICT_EQUAL, ev->fns[i], args[0] - ); - bool_t match = jerry_value_is_true(eq); - jerry_value_free(eq); - if(match) { - eventUnsubscribe(ev->event, MODULE_EVENT_TRAMPOLINES[i]); - jerry_value_free(ev->fns[i]); - ev->fns[i] = jerry_undefined(); - return jerry_value_copy(callInfo->this_value); - } - } - return jerry_value_copy(callInfo->this_value); -} - -moduleBaseFunction(moduleEventWait) { - jsevent_t *ev = (jsevent_t *)scriptProtoGetValue( - &MODULE_EVENT_PROTO, callInfo->this_value - ); - if(!ev || !ev->event) { - return moduleBaseThrow("Event.wait: invalid event"); - } - if(MODULE_EVENT_PENDING_COUNT >= MODULE_EVENT_PENDING_MAX) { - return moduleBaseThrow("Event.wait: too many pending awaits"); - } - if(!scriptPromisePendHas( - MODULE_EVENT_PENDING, MODULE_EVENT_PENDING_COUNT, ev->event - )) { - if(ev->event->count >= ev->event->size) { - return moduleBaseThrow( - "Event.wait: event subscriber capacity exceeded" - ); - } - eventSubscribe(ev->event, moduleEventWaitFire, ev->event); - } - jerry_value_t promise = jerry_promise(); - scriptPromisePendAdd( - MODULE_EVENT_PENDING, &MODULE_EVENT_PENDING_COUNT, - MODULE_EVENT_PENDING_MAX, ev->event, promise - ); - return promise; -} - -jerry_value_t moduleEventCreate(event_t *event) { - assertNotNull(event, "moduleEventCreate: event must not be NULL"); - jsevent_t ev; - ev.event = event; - for(uint32_t i = 0; i < MODULE_EVENT_MAX_SLOTS; i++) { - ev.fns[i] = jerry_undefined(); - } - return scriptProtoCreateValue(&MODULE_EVENT_PROTO, &ev); -} - -jerry_value_t moduleEventGetOrCreate( - const jerry_call_info_t *callInfo, - event_t *event, - const char_t *pinKey -) { - jerry_value_t keyStr = jerry_string_sz(pinKey); - jerry_value_t existing = jerry_object_get( - callInfo->this_value, keyStr - ); - if(!jerry_value_is_undefined(existing)) { - jerry_value_free(keyStr); - return existing; - } - jerry_value_free(existing); - jerry_value_t ev = moduleEventCreate(event); - jerry_object_set(callInfo->this_value, keyStr, ev); - jerry_value_free(keyStr); - return ev; -} - -void moduleEventWaitFire(void *params, void *user) { - event_t *event = (event_t *)user; - jerry_value_t undef = jerry_undefined(); - uint32_t n = scriptPromisePendResolve( - MODULE_EVENT_PENDING, &MODULE_EVENT_PENDING_COUNT, event, undef - ); - jerry_value_free(undef); - if(!n) return; - eventUnsubscribe(event, moduleEventWaitFire); -} - -void moduleEventInit(void) { - MODULE_EVENT_PENDING_COUNT = 0; - scriptProtoInit( - &MODULE_EVENT_PROTO, NULL, - sizeof(jsevent_t), NULL - ); - MODULE_EVENT_PROTO.info.free_cb = moduleEventFree; - scriptProtoDefineFunc(&MODULE_EVENT_PROTO, "on", moduleEventOn); - scriptProtoDefineFunc(&MODULE_EVENT_PROTO, "off", moduleEventOff); - scriptProtoDefineFunc(&MODULE_EVENT_PROTO, "wait", moduleEventWait); -} - -void moduleEventDispose(void) { - for(uint32_t i = 0; i < MODULE_EVENT_PENDING_COUNT; i++) { - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(MODULE_EVENT_PENDING[j].key == MODULE_EVENT_PENDING[i].key) { - seen = true; break; - } - } - if(!seen) { - eventUnsubscribe( - (event_t *)MODULE_EVENT_PENDING[i].key, moduleEventWaitFire - ); - } - } - scriptPromisePendFreeAll( - MODULE_EVENT_PENDING, &MODULE_EVENT_PENDING_COUNT - ); - scriptProtoDispose(&MODULE_EVENT_PROTO); -} diff --git a/src/dusk/script/module/event/moduleevent.h b/src/dusk/script/module/event/moduleevent.h deleted file mode 100644 index c8012f6c..00000000 --- a/src/dusk/script/module/event/moduleevent.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/scriptpromisepend.h" -#include "event/event.h" - -/** Maximum number of on() subscriber slots per Event. */ -#define MODULE_EVENT_MAX_SLOTS 4 - -extern scriptproto_t MODULE_EVENT_PROTO; - -/** - * Array of C trampolines for on() subscriptions. - * Slot i uses MODULE_EVENT_TRAMPOLINES[i]. - */ -extern eventcallback_t MODULE_EVENT_TRAMPOLINES[MODULE_EVENT_MAX_SLOTS]; - -/** Native data stored on each Event JS object. */ -typedef struct { - event_t *event; - jerry_value_t fns[MODULE_EVENT_MAX_SLOTS]; -} jsevent_t; - -/** - * Event trampoline used by wait(). Resolves all pending wait() promises - * for the fired event and unsubscribes itself. - * - * @param params Unused. - * @param user The event_t pointer that fired. - */ -void moduleEventWaitFire(void *params, void *user); - -/** - * GC free callback - unsubscribes all on() slots and releases JS function - * refs when the Event object is garbage collected. - * - * @param ptr Native jsevent_t pointer. - * @param info Native info (unused). - */ -void moduleEventFree(void *ptr, jerry_object_native_info_t *info); - -/** - * on(fn) - subscribes fn to the event. Returns this for chaining. - * @param args[0] Function to call when the event fires. - */ -moduleBaseFunction(moduleEventOn); - -/** - * off(fn) - removes a previously subscribed fn. Returns this for chaining. - * @param args[0] The same function reference passed to on(). - */ -moduleBaseFunction(moduleEventOff); - -/** - * wait() - returns a Promise that resolves the next time the event fires. - */ -moduleBaseFunction(moduleEventWait); - -/** - * Wraps a C event_t pointer in a new JS Event object. - * - * @param event The event to wrap. Must outlive the returned JS value. - * @return A new JS Event instance. - */ -jerry_value_t moduleEventCreate(event_t *event); - -/** - * Returns the Event object pinned at pinKey on the parent JS object, - * creating and pinning a new one on first access. - * - * @param callInfo The call info - this_value is the parent object. - * @param event The C event_t to wrap. Must outlive the parent object. - * @param pinKey Property name used to cache the Event on the parent. - * @return A JS Event value (caller must free). - */ -jerry_value_t moduleEventGetOrCreate( - const jerry_call_info_t *callInfo, - event_t *event, - const char_t *pinKey -); - -/** Initializes the Event module and registers the global Event prototype. */ -void moduleEventInit(void); - -/** - * Disposes the Event module, rejecting any pending wait() promises and - * cleaning up. - */ -void moduleEventDispose(void); diff --git a/src/dusk/script/module/input/CMakeLists.txt b/src/dusk/script/module/input/CMakeLists.txt deleted file mode 100644 index fde1ea90..00000000 --- a/src/dusk/script/module/input/CMakeLists.txt +++ /dev/null @@ -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 - moduleinput.c -) diff --git a/src/dusk/script/module/input/moduleinput.c b/src/dusk/script/module/input/moduleinput.c deleted file mode 100644 index f927e330..00000000 --- a/src/dusk/script/module/input/moduleinput.c +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleinput.h" -#include - -scriptproto_t MODULE_INPUT_PROTO; - -moduleBaseFunction(moduleInputIsDown) { - moduleInputRequireAction(0, "Input.isDown"); - return jerry_boolean(inputIsDown((inputaction_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleInputWasDown) { - moduleInputRequireAction(0, "Input.wasDown"); - return jerry_boolean(inputWasDown((inputaction_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleInputPressed) { - moduleInputRequireAction(0, "Input.pressed"); - return jerry_boolean(inputPressed((inputaction_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleInputReleased) { - moduleInputRequireAction(0, "Input.released"); - return jerry_boolean(inputReleased((inputaction_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleInputGetValue) { - moduleInputRequireAction(0, "Input.getValue"); - return jerry_number( - inputGetCurrentValue((inputaction_t)moduleBaseArgInt(0)) - ); -} - -moduleBaseFunction(moduleInputAxis) { - moduleInputRequireAction(0, "Input.axis"); - moduleInputRequireAction(1, "Input.axis"); - return jerry_number(inputAxis( - (inputaction_t)moduleBaseArgInt(0), - (inputaction_t)moduleBaseArgInt(1) - )); -} - -moduleBaseFunction(moduleInputBind) { - moduleBaseRequireArgs(2); - moduleBaseRequireString(0); - if(!jerry_value_is_number(args[1])) { - return moduleBaseThrow("Input.bind: action must be a number"); - } - const inputaction_t action = (inputaction_t)moduleBaseArgInt(1); - if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - return moduleBaseThrow("Input.bind: invalid action"); - } - - char_t name[128]; - moduleBaseToString(args[0], name, sizeof(name)); - if(name[0] == '\0') { - return moduleBaseThrow("Input.bind: button name cannot be empty"); - } - - inputbutton_t btn = inputButtonGetByName(name); - if(btn.type == INPUT_BUTTON_TYPE_NONE) { - return moduleBaseThrow("Input.bind: unknown button name"); - } - - inputBind(btn, action); - return jerry_undefined(); -} - -void moduleInputInit(void) { - scriptProtoInit(&MODULE_INPUT_PROTO, "Input", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "isDown", moduleInputIsDown - ); - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "wasDown", moduleInputWasDown - ); - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "pressed", moduleInputPressed - ); - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "released", moduleInputReleased - ); - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "getValue", moduleInputGetValue - ); - scriptProtoDefineStaticFunc(&MODULE_INPUT_PROTO, "axis", moduleInputAxis); - scriptProtoDefineStaticFunc(&MODULE_INPUT_PROTO, "bind", moduleInputBind); - - jerry_value_t result = jerry_eval( - (const jerry_char_t *)INPUT_ACTION_SCRIPT, - strlen(INPUT_ACTION_SCRIPT), - JERRY_PARSE_NO_OPTS - ); - jerry_value_free(result); -} - -void moduleInputDispose(void) { - scriptProtoDispose(&MODULE_INPUT_PROTO); -} diff --git a/src/dusk/script/module/input/moduleinput.h b/src/dusk/script/module/input/moduleinput.h deleted file mode 100644 index d649ee2d..00000000 --- a/src/dusk/script/module/input/moduleinput.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "input/input.h" -#include "input/inputbutton.h" - -extern scriptproto_t MODULE_INPUT_PROTO; - -/** - * Validates an inputaction_t argument and returns a type error if invalid. - * - * @param i Argument index. - * @param ctx Context string for error messages. - */ -#define moduleInputRequireAction(i, ctx) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow(ctx ": action must be a number"); \ - } \ - const inputaction_t _act = (inputaction_t)moduleBaseArgInt(i); \ - if(_act <= INPUT_ACTION_NULL || _act >= INPUT_ACTION_COUNT) { \ - return moduleBaseThrow(ctx ": invalid action"); \ - } \ -} while(0) - -/** - * Input.isDown(action) - true while the action is held this frame. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputIsDown); - -/** - * Input.wasDown(action) - true if the action was held last frame. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputWasDown); - -/** - * Input.pressed(action) - true on the first frame the action is held. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputPressed); - -/** - * Input.released(action) - true on the first frame the action is released. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputReleased); - -/** - * Input.getValue(action) - returns the analog value (0-1) of the action. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputGetValue); - -/** - * Input.axis(negative, positive) - returns a signed axis value in [-1, 1]. - * @param args[0] Negative action constant. - * @param args[1] Positive action constant. - */ -moduleBaseFunction(moduleInputAxis); - -/** - * Input.bind(buttonName, action) - binds a named button to an action. - * @param args[0] Button name string. - * @param args[1] Input action constant. - */ -moduleBaseFunction(moduleInputBind); - -/** - * Initializes the Input module, registers Input.isDown/wasDown/pressed/ - * released/getValue/axis/bind, and evaluates INPUT_ACTION_SCRIPT to populate - * action constants in the global scope. - */ -void moduleInputInit(void); - -/** - * Disposes the Input module. - */ -void moduleInputDispose(void); diff --git a/src/dusk/script/module/item/CMakeLists.txt b/src/dusk/script/module/item/CMakeLists.txt deleted file mode 100644 index 1e292daf..00000000 --- a/src/dusk/script/module/item/CMakeLists.txt +++ /dev/null @@ -1,10 +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 - moduleitem.c - modulebackpack.c -) diff --git a/src/dusk/script/module/item/modulebackpack.c b/src/dusk/script/module/item/modulebackpack.c deleted file mode 100644 index c0b0702a..00000000 --- a/src/dusk/script/module/item/modulebackpack.c +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulebackpack.h" -#include "moduleitem.h" -#include "item/backpack.h" - -scriptproto_t MODULE_BACKPACK_PROTO; - -moduleBaseFunction(moduleBackpackGetIsFull) { - return jerry_boolean(inventoryIsFull(&BACKPACK)); -} - -moduleBaseFunction(moduleBackpackGetCount) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Backpack.getCount"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_number((double)inventoryGetCount(&BACKPACK, id)); -} - -moduleBaseFunction(moduleBackpackHas) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Backpack.has"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_boolean(inventoryItemExists(&BACKPACK, id)); -} - -moduleBaseFunction(moduleBackpackIsItemFull) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Backpack.isItemFull"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_boolean(inventoryItemFull(&BACKPACK, id)); -} - -moduleBaseFunction(moduleBackpackSet) { - moduleBaseRequireArgs(2); - moduleItemRequireId(0, "Backpack.set"); - moduleBaseRequireNumber(1); - const int32_t qty = moduleBaseArgInt(1); - if(qty < 0 || qty > (int32_t)ITEM_STACK_QUANTITY_MAX) { - return moduleBaseThrow("Backpack.set: quantity out of range (0-255)"); - } - inventorySet(&BACKPACK, (itemid_t)moduleBaseArgInt(0), (uint8_t)qty); - return jerry_undefined(); -} - -moduleBaseFunction(moduleBackpackAdd) { - moduleBaseRequireArgs(2); - moduleItemRequireId(0, "Backpack.add"); - moduleBaseRequireNumber(1); - const int32_t qty = moduleBaseArgInt(1); - if(qty < 1 || qty > (int32_t)ITEM_STACK_QUANTITY_MAX) { - return moduleBaseThrow("Backpack.add: quantity out of range (1-255)"); - } - inventoryAdd(&BACKPACK, (itemid_t)moduleBaseArgInt(0), (uint8_t)qty); - return jerry_undefined(); -} - -moduleBaseFunction(moduleBackpackRemove) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Backpack.remove"); - inventoryRemove(&BACKPACK, (itemid_t)moduleBaseArgInt(0)); - return jerry_undefined(); -} - -moduleBaseFunction(moduleBackpackSort) { - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - const inventorysort_t sortBy = (inventorysort_t)moduleBaseArgInt(0); - if((int32_t)sortBy < 0 || sortBy >= INVENTORY_SORT_COUNT) { - return moduleBaseThrow("Backpack.sort: invalid sort type"); - } - const bool_t reverse = (argc > 1 && jerry_value_is_true(args[1])); - inventorySort(&BACKPACK, sortBy, reverse); - return jerry_undefined(); -} - -void moduleBackpackInit(void) { - scriptProtoInit( - &MODULE_BACKPACK_PROTO, "Backpack", sizeof(uint8_t), NULL - ); - - scriptProtoDefineStaticProp( - &MODULE_BACKPACK_PROTO, "isFull", moduleBackpackGetIsFull, NULL - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "getCount", moduleBackpackGetCount - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "has", moduleBackpackHas - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "isItemFull", moduleBackpackIsItemFull - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "set", moduleBackpackSet - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "add", moduleBackpackAdd - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "remove", moduleBackpackRemove - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "sort", moduleBackpackSort - ); - - moduleBaseSetInt( - "INVENTORY_SORT_BY_ID", (int32_t)INVENTORY_SORT_BY_ID - ); - moduleBaseSetInt( - "INVENTORY_SORT_BY_TYPE", (int32_t)INVENTORY_SORT_BY_TYPE - ); -} - -void moduleBackpackDispose(void) { - scriptProtoDispose(&MODULE_BACKPACK_PROTO); -} diff --git a/src/dusk/script/module/item/modulebackpack.h b/src/dusk/script/module/item/modulebackpack.h deleted file mode 100644 index 5bbcd715..00000000 --- a/src/dusk/script/module/item/modulebackpack.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" - -extern scriptproto_t MODULE_BACKPACK_PROTO; - -/** - * Backpack.isFull - true when all storage slots are occupied. - */ -moduleBaseFunction(moduleBackpackGetIsFull); - -/** - * Backpack.getCount(itemId) - quantity of itemId (0 if absent). - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleBackpackGetCount); - -/** - * Backpack.has(itemId) - true if itemId is present with quantity > 0. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleBackpackHas); - -/** - * Backpack.isItemFull(itemId) - true if the stack is at max quantity. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleBackpackIsItemFull); - -/** - * Backpack.set(itemId, quantity) - set quantity; removes when 0. - * - * @param args[0] Item ID constant (ITEM_ID_*). - * @param args[1] Quantity 0-255. - */ -moduleBaseFunction(moduleBackpackSet); - -/** - * Backpack.add(itemId, quantity) - add quantity units of itemId. - * - * @param args[0] Item ID constant (ITEM_ID_*). - * @param args[1] Quantity to add (1-255). - */ -moduleBaseFunction(moduleBackpackAdd); - -/** - * Backpack.remove(itemId) - removes itemId entirely from the backpack. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleBackpackRemove); - -/** - * Backpack.sort(sortBy[, reverse]) - sort the backpack contents. - * - * @param args[0] Sort type (INVENTORY_SORT_BY_ID or - * INVENTORY_SORT_BY_TYPE). - * @param args[1] Optional boolean; true to reverse order. - */ -moduleBaseFunction(moduleBackpackSort); - -/** - * Initializes the Backpack module, registers all Backpack methods, and - * sets INVENTORY_SORT_BY_ID / INVENTORY_SORT_BY_TYPE as globals. - */ -void moduleBackpackInit(void); - -/** - * Disposes the Backpack module. - */ -void moduleBackpackDispose(void); diff --git a/src/dusk/script/module/item/moduleitem.c b/src/dusk/script/module/item/moduleitem.c deleted file mode 100644 index 05512814..00000000 --- a/src/dusk/script/module/item/moduleitem.c +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleitem.h" -#include - -scriptproto_t MODULE_ITEM_PROTO; - -moduleBaseFunction(moduleItemGetName) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Item.getName"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_string_sz(ITEMS[id].name); -} - -moduleBaseFunction(moduleItemGetType) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Item.getType"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_number((double)ITEMS[id].type); -} - -void moduleItemInit(void) { - scriptProtoInit(&MODULE_ITEM_PROTO, "Item", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticFunc( - &MODULE_ITEM_PROTO, "getName", moduleItemGetName - ); - scriptProtoDefineStaticFunc( - &MODULE_ITEM_PROTO, "getType", moduleItemGetType - ); - - jerry_value_t result = jerry_eval( - (const jerry_char_t *)ITEM_SCRIPT, - strlen(ITEM_SCRIPT), - JERRY_PARSE_NO_OPTS - ); - jerry_value_free(result); -} - -void moduleItemDispose(void) { - scriptProtoDispose(&MODULE_ITEM_PROTO); -} diff --git a/src/dusk/script/module/item/moduleitem.h b/src/dusk/script/module/item/moduleitem.h deleted file mode 100644 index 752b550c..00000000 --- a/src/dusk/script/module/item/moduleitem.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "item/item.h" - -extern scriptproto_t MODULE_ITEM_PROTO; - -/** - * Validates an itemid_t argument and returns a type error if invalid. - * - * @param i Argument index. - * @param ctx Context string for error messages. - */ -#define moduleItemRequireId(i, ctx) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow(ctx ": itemId must be a number"); \ - } \ - const itemid_t _id = (itemid_t)moduleBaseArgInt(i); \ - if(_id <= ITEM_ID_NULL || _id >= ITEM_ID_COUNT) { \ - return moduleBaseThrow(ctx ": invalid item ID"); \ - } \ -} while(0) - -/** - * Item.getName(itemId) - returns the item's string name. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleItemGetName); - -/** - * Item.getType(itemId) - returns the item's type constant. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleItemGetType); - -/** - * Initializes the Item module, registers Item.getName/getType, and - * evaluates ITEM_SCRIPT to populate ITEM_ID_* and ITEM_TYPE_* constants - * as globals in the script scope. - */ -void moduleItemInit(void); - -/** - * Disposes the Item module. - */ -void moduleItemDispose(void); diff --git a/src/dusk/script/module/locale/CMakeLists.txt b/src/dusk/script/module/locale/CMakeLists.txt deleted file mode 100644 index 7eb305af..00000000 --- a/src/dusk/script/module/locale/CMakeLists.txt +++ /dev/null @@ -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 - modulelocale.c -) diff --git a/src/dusk/script/module/locale/modulelocale.c b/src/dusk/script/module/locale/modulelocale.c deleted file mode 100644 index f824b9d2..00000000 --- a/src/dusk/script/module/locale/modulelocale.c +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulelocale.h" -#include "asset/loader/locale/assetlocaleloader.h" -#include "util/string.h" -#include - -#define MODULE_LOCALE_SUBSTITUTION_MAX 4 -#define MODULE_LOCALE_BUFFER_SIZE 512 -#define MODULE_LOCALE_ID_SIZE 256 - -scriptproto_t MODULE_LOCALE_PROTO; - -moduleBaseFunction(moduleLocaleGetText) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - - if(LOCALE.entry == NULL) { - return moduleBaseThrow("Locale.getText: no locale loaded"); - } - - char_t id[MODULE_LOCALE_ID_SIZE]; - moduleBaseToString(args[0], id, sizeof(id)); - - // Second arg is optional plural count. - int32_t plural = 1; - jerry_length_t subStart = 1; - if(argc > 1 && jerry_value_is_number(args[1])) { - plural = moduleBaseArgInt(1); - subStart = 2; - } - - // Collect up to MODULE_LOCALE_SUBSTITUTION_MAX substitution args. - assetlocalearg_t subArgs[MODULE_LOCALE_SUBSTITUTION_MAX]; - char_t subStrings[MODULE_LOCALE_SUBSTITUTION_MAX][128]; - size_t subCount = 0; - - for(jerry_length_t i = subStart; i < argc; i++) { - if(subCount >= MODULE_LOCALE_SUBSTITUTION_MAX) break; - - if(jerry_value_is_string(args[i])) { - moduleBaseToString( - args[i], subStrings[subCount], sizeof(subStrings[subCount]) - ); - subArgs[subCount].type = ASSET_LOCALE_ARG_STRING; - subArgs[subCount].stringValue = subStrings[subCount]; - } else if(jerry_value_is_number(args[i])) { - double n = jerry_value_as_number(args[i]); - if(floor(n) == n) { - subArgs[subCount].type = ASSET_LOCALE_ARG_INT; - subArgs[subCount].intValue = (int32_t)n; - } else { - subArgs[subCount].type = ASSET_LOCALE_ARG_FLOAT; - subArgs[subCount].floatValue = (float_t)n; - } - } else { - continue; - } - subCount++; - } - - char_t buf[MODULE_LOCALE_BUFFER_SIZE]; - assetLocaleGetStringWithArgs( - &LOCALE.entry->data.locale, - id, - plural, - buf, - sizeof(buf), - subCount > 0 ? subArgs : NULL, - subCount - ); - - return jerry_string_sz(buf); -} - -moduleBaseFunction(moduleLocaleSetLocale) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - - char_t name[64]; - moduleBaseToString(args[0], name, sizeof(name)); - - const localeinfo_t *locale = NULL; - if(stringEquals(name, LOCALE_EN_US.name)) locale = &LOCALE_EN_US; - - if(locale == NULL) { - return moduleBaseThrow("Locale.setLocale: unknown locale name"); - } - - localeManagerSetLocale(locale); - return jerry_undefined(); -} - -void moduleLocaleInit(void) { - scriptProtoInit( - &MODULE_LOCALE_PROTO, "Locale", sizeof(uint8_t), NULL - ); - - scriptProtoDefineStaticFunc( - &MODULE_LOCALE_PROTO, "getText", moduleLocaleGetText - ); - scriptProtoDefineStaticFunc( - &MODULE_LOCALE_PROTO, "setLocale", moduleLocaleSetLocale - ); -} - -void moduleLocaleDispose(void) { - scriptProtoDispose(&MODULE_LOCALE_PROTO); -} diff --git a/src/dusk/script/module/locale/modulelocale.h b/src/dusk/script/module/locale/modulelocale.h deleted file mode 100644 index 1504dcf2..00000000 --- a/src/dusk/script/module/locale/modulelocale.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "locale/localemanager.h" - -extern scriptproto_t MODULE_LOCALE_PROTO; - -/** - * Locale.getText(id[, pluralCount[, arg1, ...]]) - returns the - * translated string for the given message ID. - * - * @param args[0] Message ID string. - * @param args[1] Optional plural count (number). Defaults to 1. - * @param args[2..5] Optional substitution args (string or number). - */ -moduleBaseFunction(moduleLocaleGetText); - -/** - * Locale.setLocale(name) - switches the active locale by name. - * - * @param args[0] Locale name string, e.g. "en-US". - */ -moduleBaseFunction(moduleLocaleSetLocale); - -/** - * Initializes the Locale module and registers Locale.getText/setLocale. - */ -void moduleLocaleInit(void); - -/** - * Disposes the Locale module. - */ -void moduleLocaleDispose(void); diff --git a/src/dusk/script/module/math/CMakeLists.txt b/src/dusk/script/module/math/CMakeLists.txt deleted file mode 100644 index da510807..00000000 --- a/src/dusk/script/module/math/CMakeLists.txt +++ /dev/null @@ -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 - modulevec3.c -) diff --git a/src/dusk/script/module/math/modulevec3.c b/src/dusk/script/module/math/modulevec3.c deleted file mode 100644 index 1e0090b1..00000000 --- a/src/dusk/script/module/math/modulevec3.c +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulevec3.h" - -scriptproto_t MODULE_VEC3_PROTO; - -float_t *moduleVec3Get(const jerry_call_info_t *callInfo) { - return (float_t *)scriptProtoGetValue( - &MODULE_VEC3_PROTO, callInfo->this_value - ); -} - -float_t *moduleVec3From(const jerry_value_t val) { - return (float_t *)scriptProtoGetValue(&MODULE_VEC3_PROTO, val); -} - -jerry_value_t moduleVec3Push(const vec3 v) { - return scriptProtoCreateValue(&MODULE_VEC3_PROTO, v); -} - -moduleBaseFunction(moduleVec3Constructor) { - float_t *ptr = (float_t *)memoryAllocate(sizeof(vec3)); - ptr[0] = moduleBaseOptFloat(0, 0.0f); - ptr[1] = moduleBaseOptFloat(1, 0.0f); - ptr[2] = moduleBaseOptFloat(2, 0.0f); - jerry_object_set_native_ptr( - callInfo->this_value, &MODULE_VEC3_PROTO.info, ptr - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleVec3GetX) { - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - return jerry_number((double)v[0]); -} - -moduleBaseFunction(moduleVec3SetX) { - moduleBaseRequireArgs(1); - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - v[0] = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleVec3GetY) { - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - return jerry_number((double)v[1]); -} - -moduleBaseFunction(moduleVec3SetY) { - moduleBaseRequireArgs(1); - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - v[1] = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleVec3GetZ) { - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - return jerry_number((double)v[2]); -} - -moduleBaseFunction(moduleVec3SetZ) { - moduleBaseRequireArgs(1); - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - v[2] = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleVec3ToString) { - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_string_sz("Vec3:invalid"); - char_t buf[64]; - snprintf(buf, sizeof(buf), "Vec3(%g, %g, %g)", - (double)v[0], (double)v[1], (double)v[2] - ); - return jerry_string_sz(buf); -} - -void moduleVec3Init(void) { - scriptProtoInit( - &MODULE_VEC3_PROTO, "Vec3", - sizeof(vec3), moduleVec3Constructor - ); - scriptProtoDefineProp( - &MODULE_VEC3_PROTO, "x", moduleVec3GetX, moduleVec3SetX - ); - scriptProtoDefineProp( - &MODULE_VEC3_PROTO, "y", moduleVec3GetY, moduleVec3SetY - ); - scriptProtoDefineProp( - &MODULE_VEC3_PROTO, "z", moduleVec3GetZ, moduleVec3SetZ - ); - scriptProtoDefineToString(&MODULE_VEC3_PROTO, moduleVec3ToString); -} - -void moduleVec3Dispose(void) { - scriptProtoDispose(&MODULE_VEC3_PROTO); -} diff --git a/src/dusk/script/module/math/modulevec3.h b/src/dusk/script/module/math/modulevec3.h deleted file mode 100644 index cc267bfb..00000000 --- a/src/dusk/script/module/math/modulevec3.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "util/memory.h" -#include "cglm/cglm.h" - -extern scriptproto_t MODULE_VEC3_PROTO; - -/** - * Returns the raw float[3] pointer from a Vec3 JS instance. - * - * @param callInfo Call info whose this_value is the Vec3 instance. - * @return Pointer to the vec3 data, or NULL if not a Vec3. - */ -float_t *moduleVec3Get(const jerry_call_info_t *callInfo); - -/** - * Returns the raw float[3] pointer from an arbitrary Vec3 JS value. - * - * @param val The JS value to extract from. - * @return Pointer to the vec3 data, or NULL if not a Vec3. - */ -float_t *moduleVec3From(const jerry_value_t val); - -/** - * Creates a Vec3 JS object wrapping a copy of a C vec3. - * - * @param v The source vec3. - * @return A new Vec3 JS object. - */ -jerry_value_t moduleVec3Push(const vec3 v); - -/** Vec3(x?, y?, z?) constructor. */ -moduleBaseFunction(moduleVec3Constructor); - -/** @return The x component as a number. */ -moduleBaseFunction(moduleVec3GetX); -/** Sets the x component. @param args[0] New x value. */ -moduleBaseFunction(moduleVec3SetX); - -/** @return The y component as a number. */ -moduleBaseFunction(moduleVec3GetY); -/** Sets the y component. @param args[0] New y value. */ -moduleBaseFunction(moduleVec3SetY); - -/** @return The z component as a number. */ -moduleBaseFunction(moduleVec3GetZ); -/** Sets the z component. @param args[0] New z value. */ -moduleBaseFunction(moduleVec3SetZ); - -/** @return "Vec3(x, y, z)" string. */ -moduleBaseFunction(moduleVec3ToString); - -/** - * Initializes the Vec3 module and registers the global Vec3 class. - */ -void moduleVec3Init(void); - -/** - * Disposes the Vec3 module. - */ -void moduleVec3Dispose(void); diff --git a/src/dusk/script/module/modulebase.c b/src/dusk/script/module/modulebase.c deleted file mode 100644 index c3797f6d..00000000 --- a/src/dusk/script/module/modulebase.c +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulebase.h" -#include "util/memory.h" - -jerry_object_native_info_t MODULE_BASE_NATIVE_INFO = { - .free_cb = moduleBaseFreeProto, - .number_of_references = 0, - .offset_of_references = 0 -}; - -void moduleBaseFreeProto(void *ptr, jerry_object_native_info_t *info) { - assertNotNull(ptr, "Pointer must not be null"); - assertNotNull(info, "Native info must not be null"); - memoryFree(ptr); -} - -jerry_value_t moduleBaseThrow(const char_t *message) { - assertStrLenMin(message, 1, "Error message must not be empty"); - return jerry_throw_sz(JERRY_ERROR_TYPE, message); -} - -jerry_value_t moduleBaseThrowError(const errorret_t err) { - assertNotNull(err.state, "Error state must not be NULL"); - assertNotNull(err.state->message, "Error message must not be NULL"); - jerry_value_t jsErr = jerry_throw_sz(JERRY_ERROR_TYPE, err.state->message); - errorCatch(err); - return jsErr; -} - -void moduleBaseToString(jerry_value_t val, char_t *buf, jerry_size_t buflen) { - jerry_size_t len = jerry_string_to_buffer( - val, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, buflen - 1 - ); - buf[len] = '\0'; -} - -void moduleBaseExceptionMessage( - jerry_value_t exception, - char_t *buf, - size_t buflen -) { - jerry_value_t errVal = jerry_exception_value(exception, false); - jerry_value_t errStr = jerry_value_to_string(errVal); - jerry_size_t len = jerry_string_to_buffer( - errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, (jerry_size_t)(buflen - 1) - ); - buf[len] = '\0'; - jerry_value_free(errStr); - jerry_value_free(errVal); -} - -void moduleBaseDefineMethod( - jerry_value_t obj, - const char_t *name, - jerry_external_handler_t fn -) { - jerry_value_t key = jerry_string_sz(name); - jerry_value_t func = jerry_function_external(fn); - jerry_object_set(obj, key, func); - jerry_value_free(func); - jerry_value_free(key); -} - -void moduleBaseDefineGlobalMethod( - const char_t *name, - jerry_external_handler_t fn -) { - jerry_value_t global = jerry_current_realm(); - moduleBaseDefineMethod(global, name, fn); - jerry_value_free(global); -} - -void moduleBaseSetValue(const char_t *name, jerry_value_t value) { - jerry_value_t global = jerry_current_realm(); - jerry_value_t key = jerry_string_sz(name); - jerry_object_set(global, key, value); - jerry_value_free(key); - jerry_value_free(global); -} - -jerry_value_t moduleBaseWrapPointer(void *ptr) { - jerry_value_t obj = jerry_object(); - jerry_object_set_native_ptr(obj, &MODULE_BASE_NATIVE_INFO, ptr); - return obj; -} - -void *moduleBaseUnwrapPointer(jerry_value_t val) { - if(!jerry_value_is_object(val)) return NULL; - return jerry_object_get_native_ptr(val, &MODULE_BASE_NATIVE_INFO); -} - -jerry_value_t moduleBaseGetProp( - jerry_value_t obj, const char_t *name -) { - jerry_value_t key = jerry_string_sz(name); - jerry_value_t val = jerry_object_get(obj, key); - jerry_value_free(key); - return val; -} - -float_t moduleBaseValueFloat(jerry_value_t val) { - return (float_t)jerry_value_as_number(val); -} - -int32_t moduleBaseValueInt(jerry_value_t val) { - return (int32_t)jerry_value_as_number(val); -} - -void moduleBaseSetNumber(const char_t *name, double value) { - jerry_value_t global = jerry_current_realm(); - jerry_value_t key = jerry_string_sz(name); - jerry_value_t val = jerry_number(value); - jerry_object_set(global, key, val); - jerry_value_free(val); - jerry_value_free(key); - jerry_value_free(global); -} - -void moduleBaseSetInt(const char_t *name, int32_t value) { - moduleBaseSetNumber(name, (double)value); -} \ No newline at end of file diff --git a/src/dusk/script/module/modulebase.h b/src/dusk/script/module/modulebase.h deleted file mode 100644 index 71e36f11..00000000 --- a/src/dusk/script/module/modulebase.h +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "assert/assert.h" -#include "util/string.h" -#include -#include - -extern jerry_object_native_info_t MODULE_BASE_NATIVE_INFO; - -/** - * Frees a native pointer associated with a JavaScript object, uses the internal - * memory functions. - * - * @param ptr The native pointer to free. Must not be NULL. - * @param info The native info associated with the pointer. Must not be NULL. - */ -void moduleBaseFreeProto(void *ptr, jerry_object_native_info_t *info); - -/** - * Throws a JavaScript error with the given message. - * - * @param message The error message to throw. Must not be empty. - * @return A jerry_value_t representing the thrown error. - */ -jerry_value_t moduleBaseThrow(const char_t *message); - -/** - * "Converts" a dusk error to a jerry type error. - * - * @param err Native dusk error to convert. - * @return A jerry_value_t representing the thrown error. - */ -jerry_value_t moduleBaseThrowError(const errorret_t err); - -/** - * Converts a jerry value to a string and writes it to the provided buffer. - * - * @param val The jerry value to convert. - * @param buf The buffer to write the string to. - * @param buflen The length of the buffer. - */ -void moduleBaseToString(jerry_value_t val, char_t *buf, jerry_size_t buflen); - -/** - * Converts a jerry exception value to a string and writes it to the provided - * buffer. - * - * @param exception The jerry exception value to convert. - * @param buf The buffer to write the string to. - * @param buflen The length of the buffer. - */ -void moduleBaseExceptionMessage( - jerry_value_t exception, - char_t *buf, - size_t buflen -); - -/** - * Defines a method on a given object. - * - * @param obj The object to define the method on. - * @param name The name of the method. - * @param fn The function pointer to the method implementation. - */ -void moduleBaseDefineMethod( - jerry_value_t obj, - const char_t *name, - jerry_external_handler_t fn -); - -/** - * Defines a global method. - * - * @param name The name of the method. - * @param fn The function pointer to the method implementation. - */ -void moduleBaseDefineGlobalMethod( - const char_t *name, - jerry_external_handler_t fn -); - -/** - * Sets a global variable to a given jerry value. - * - * @param name The name of the global variable. - * @param value The jerry value to set the variable to. - */ -void moduleBaseSetValue(const char_t *name, jerry_value_t value); - -/** - * Wraps a native pointer in a jerry object. - * - * @param ptr The native pointer to wrap. - * @return A jerry_value_t representing the wrapped pointer. - */ -jerry_value_t moduleBaseWrapPointer(void *ptr); - -/** - * Unwraps a native pointer from a jerry object. - * - * @param val The jerry value to unwrap the pointer from. - * @return The unwrapped native pointer. - */ -void *moduleBaseUnwrapPointer(jerry_value_t val); - -/** - * Gets a property from a jerry object as a jerry value. - * - * @param obj The object to get the property from. - * @param name The name of the property to get. - * @return The value of the property. - */ -jerry_value_t moduleBaseGetProp( - jerry_value_t obj, const char_t *name -); - -/** - * Converts a jerry value to a float. - * - * @param val The jerry value to convert. - * @return The converted float value. - */ -float_t moduleBaseValueFloat(jerry_value_t val); - -/** - * Converts a jerry value to an int. - * - * @param val The jerry value to convert. - * @return The converted int value. - */ -int32_t moduleBaseValueInt(jerry_value_t val); - -/** - * Sets a global variable to a given number value. - * - * @param name The name of the global variable. - * @param value The number value to set the variable to. - */ -void moduleBaseSetNumber(const char_t *name, double value); - -/** - * Sets a global variable to a given int value. - * - * @param name The name of the global variable. - * @param value The int value to set the variable to. - */ -void moduleBaseSetInt(const char_t *name, int32_t value); - -#define moduleBaseFunction(name) \ - jerry_value_t name( \ - const jerry_call_info_t *callInfo, \ - const jerry_value_t args[], \ - const jerry_length_t argc) - -#define moduleBaseRequireArgs(n) do { \ - if(argc < (jerry_length_t)(n)) { \ - return moduleBaseThrow("Expected at least " #n " argument(s)"); \ - } \ -} while(0) - -#define moduleBaseRequireNumber(i) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow("Expected number argument"); \ - } \ -} while(0) - -#define moduleBaseRequireString(i) do { \ - if(!jerry_value_is_string(args[(i)])) { \ - return moduleBaseThrow("Expected string argument"); \ - } \ -} while(0) - -#define moduleBaseArgFloat(i) ((float_t)jerry_value_as_number(args[(i)])) -#define moduleBaseArgInt(i) ((int32_t)jerry_value_as_number(args[(i)])) -#define moduleBaseArgBool(i) (jerry_value_is_true(args[(i)])) - -#define moduleBaseOptFloat(i, def) \ - ((jerry_length_t)(i) < argc && jerry_value_is_number(args[(i)]) \ - ? (float_t)jerry_value_as_number(args[(i)]) : (def)) - -#define moduleBaseOptInt(i, def) \ - ((jerry_length_t)(i) < argc && jerry_value_is_number(args[(i)]) \ - ? (int32_t)jerry_value_as_number(args[(i)]) : (def)) diff --git a/src/dusk/script/module/modulelist.c b/src/dusk/script/module/modulelist.c deleted file mode 100644 index 1a686172..00000000 --- a/src/dusk/script/module/modulelist.c +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulelist.h" -#include "script/module/animation/moduleanimation.h" -#include "script/module/animation/moduleeasing.h" -#include "script/module/asset/moduleasset.h" -#include "script/module/console/moduleconsole.h" -#include "script/module/display/modulecolor.h" -#include "script/module/display/modulescreen.h" -#include "script/module/engine/moduleengine.h" -#include "script/module/engine/moduleframe.h" -#include "script/module/event/moduleevent.h" -#include "script/module/engine/moduletimeout.h" -#include "script/module/entity/moduleentity.h" -#include "script/module/input/moduleinput.h" -#include "script/module/item/moduleitem.h" -#include "script/module/item/modulebackpack.h" -#include "script/module/locale/modulelocale.h" -#include "script/module/save/modulesave.h" -#include "script/module/math/modulevec3.h" -#include "script/module/overworld/moduleoverworld.h" -#include "script/module/require/modulerequire.h" -#include "script/module/scene/modulescene.h" -#include "script/module/story/modulestory.h" -#include "script/module/system/modulesystem.h" -#include "script/module/ui/modulefullbox.h" -#include "script/module/ui/moduletextbox.h" - - -void moduleListInit(void) { - moduleAnimationInit(); - moduleEasingInit(); - moduleEventInit(); - moduleTextureInit(); - moduleColorInit(); - moduleAssetInit(); - moduleConsoleInit(); - moduleScreenInit(); - moduleEngineInit(); - moduleFrameInit(); - moduleTimeoutInit(); - moduleVec3Init(); - moduleEntityInit(); - moduleInputInit(); - moduleItemInit(); - moduleBackpackInit(); - moduleLocaleInit(); - moduleSaveInit(); - moduleOverworldInit(); - moduleRequireInit(); - moduleSceneInit(); - moduleStoryInit(); - moduleSystemInit(); - moduleFullboxInit(); - moduleTextboxInit(); -} - -void moduleListUpdate(void) { - moduleFrameFlush(); - moduleTimeoutFlush(); -} - -void moduleListDispose(void) { - moduleFullboxDispose(); - moduleTextboxDispose(); - moduleSystemDispose(); - moduleOverworldDispose(); - moduleEasingDispose(); - moduleAnimationDispose(); - moduleStoryDispose(); - moduleSceneDispose(); - moduleRequireDispose(); - moduleSaveDispose(); - moduleLocaleDispose(); - moduleBackpackDispose(); - moduleItemDispose(); - moduleInputDispose(); - moduleEntityDispose(); - moduleVec3Dispose(); - moduleTimeoutDispose(); - moduleFrameDispose(); - moduleEngineDispose(); - moduleScreenDispose(); - moduleConsoleDispose(); - moduleAssetDispose(); - moduleColorDispose(); - moduleTextureDispose(); - moduleEventDispose(); -} diff --git a/src/dusk/script/module/modulelist.h b/src/dusk/script/module/modulelist.h deleted file mode 100644 index 71fd3b9c..00000000 --- a/src/dusk/script/module/modulelist.h +++ /dev/null @@ -1,25 +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" - -/** - * Initializes all of the internal (C) script modules. - */ -void moduleListInit(void); - -/** - * Flushes per-frame module state. Must be called once per frame before - * jerry_run_jobs() so that frame() promises resolve in the same tick. - */ -void moduleListUpdate(void); - -/** - * Disposes all of the internal (C) script modules. - */ -void moduleListDispose(void); \ No newline at end of file diff --git a/src/dusk/script/module/overworld/CMakeLists.txt b/src/dusk/script/module/overworld/CMakeLists.txt deleted file mode 100644 index 10af18ad..00000000 --- a/src/dusk/script/module/overworld/CMakeLists.txt +++ /dev/null @@ -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 - moduleoverworld.c -) diff --git a/src/dusk/script/module/overworld/moduleoverworld.c b/src/dusk/script/module/overworld/moduleoverworld.c deleted file mode 100644 index 8bf23d2f..00000000 --- a/src/dusk/script/module/overworld/moduleoverworld.c +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleoverworld.h" -#include "overworld/map.h" - -scriptproto_t MODULE_OVERWORLD_PROTO; - -moduleBaseFunction(moduleOverworldLoadMap) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - char_t handle[MAP_HANDLE_MAX]; - moduleBaseToString(args[0], handle, (jerry_size_t)MAP_HANDLE_MAX); - errorret_t err = mapLoad(handle); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleOverworldIsLoaded) { - return jerry_boolean(mapIsLoaded()); -} - -moduleBaseFunction(moduleOverworldSetPosition) { - moduleBaseRequireArgs(3); - moduleBaseRequireNumber(0); - moduleBaseRequireNumber(1); - moduleBaseRequireNumber(2); - tilepos_t pos; - pos.x = (tileunit_t)moduleBaseArgInt(0); - pos.y = (tileunit_t)moduleBaseArgInt(1); - pos.z = (tileunit_t)moduleBaseArgInt(2); - errorret_t err = mapPositionSet(pos); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleOverworldUpdate) { - errorret_t err = mapUpdate(); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleOverworldJsDispose) { - mapDispose(); - return jerry_undefined(); -} - -void moduleOverworldInit(void) { - scriptProtoInit(&MODULE_OVERWORLD_PROTO, "Overworld", 0, NULL); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "loadMap", moduleOverworldLoadMap - ); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "isLoaded", moduleOverworldIsLoaded - ); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "setPosition", moduleOverworldSetPosition - ); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "update", moduleOverworldUpdate - ); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "dispose", moduleOverworldJsDispose - ); -} - -void moduleOverworldDispose(void) { - scriptProtoDispose(&MODULE_OVERWORLD_PROTO); -} diff --git a/src/dusk/script/module/overworld/moduleoverworld.h b/src/dusk/script/module/overworld/moduleoverworld.h deleted file mode 100644 index 19380c5a..00000000 --- a/src/dusk/script/module/overworld/moduleoverworld.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" - -extern scriptproto_t MODULE_OVERWORLD_PROTO; - -/** - * Overworld.loadMap(handle) - loads the map with the given handle string. - * Disposes any currently loaded map first. - * - * @param args[0] Map handle string (max 31 chars). - */ -moduleBaseFunction(moduleOverworldLoadMap); - -/** - * Overworld.isLoaded() - returns true when a map is currently loaded. - * - * @return boolean - */ -moduleBaseFunction(moduleOverworldIsLoaded); - -/** - * Overworld.setPosition(x, y, z) - slides the chunk window to the tile - * position (x, y, z). - * - * @param args[0] Tile X (integer). - * @param args[1] Tile Y (integer). - * @param args[2] Tile Z (integer). - */ -moduleBaseFunction(moduleOverworldSetPosition); - -/** - * Overworld.update() - advances the map one tick. - */ -moduleBaseFunction(moduleOverworldUpdate); - -/** - * Overworld.dispose() - unloads the current map. - */ -moduleBaseFunction(moduleOverworldJsDispose); - -/** - * Initializes the Overworld module and registers the Overworld global. - */ -void moduleOverworldInit(void); - -/** - * Disposes the Overworld module. - */ -void moduleOverworldDispose(void); diff --git a/src/dusk/script/module/require/CMakeLists.txt b/src/dusk/script/module/require/CMakeLists.txt deleted file mode 100644 index c063ce82..00000000 --- a/src/dusk/script/module/require/CMakeLists.txt +++ /dev/null @@ -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 - modulerequire.c -) diff --git a/src/dusk/script/module/require/modulerequire.c b/src/dusk/script/module/require/modulerequire.c deleted file mode 100644 index 36956cd5..00000000 --- a/src/dusk/script/module/require/modulerequire.c +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulerequire.h" -#include "script/scriptpromisepend.h" -#include "util/memory.h" -#include "util/string.h" -#include "assert/assert.h" - -#define MODULE_REQUIRE_ASYNC_MAX 16 - -static scriptpromisepend_t MODULE_REQUIRE_ASYNC_PEND[MODULE_REQUIRE_ASYNC_MAX]; -static uint32_t MODULE_REQUIRE_ASYNC_PEND_COUNT = 0; - -void moduleRequireAsyncOnLoaded(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)user; - eventUnsubscribe(&entry->onLoaded, moduleRequireAsyncOnLoaded); - eventUnsubscribe(&entry->onError, moduleRequireAsyncOnError); - - jerry_value_t exports = ( - jerry_value_is_undefined(entry->data.script.exports) ? - jerry_undefined() : - jerry_value_copy(entry->data.script.exports) - ); - uint32_t n = scriptPromisePendResolve( - MODULE_REQUIRE_ASYNC_PEND, &MODULE_REQUIRE_ASYNC_PEND_COUNT, - entry, exports - ); - jerry_value_free(exports); - for(uint32_t i = 0; i < n; i++) assetUnlockEntry(entry); -} - -void moduleRequireAsyncOnError(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)user; - eventUnsubscribe(&entry->onLoaded, moduleRequireAsyncOnLoaded); - eventUnsubscribe(&entry->onError, moduleRequireAsyncOnError); - - jerry_value_t errStr = jerry_string_sz("Module load failed"); - uint32_t n = scriptPromisePendReject( - MODULE_REQUIRE_ASYNC_PEND, &MODULE_REQUIRE_ASYNC_PEND_COUNT, - entry, errStr - ); - jerry_value_free(errStr); - for(uint32_t i = 0; i < n; i++) assetUnlockEntry(entry); -} - -jerry_value_t moduleRequireFunc( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc -) { - assertNotNull(callInfo, "callInfo must not be null."); - assertNotNull(args, "args must not be null."); - - if(argc < 1 || !jerry_value_is_string(args[0])) { - return moduleBaseThrow("Expected a string argument for module name."); - } - if(jerry_string_size(args[0], JERRY_ENCODING_UTF8) >= ASSET_FILE_NAME_MAX) { - return moduleBaseThrow("Module name too long."); - } - - char_t moduleName[ASSET_FILE_NAME_MAX]; - moduleBaseToString(args[0], moduleName, sizeof(moduleName)); - - assetloaderinput_t input; - input.script.nothing = NULL; - assetentry_t *entry = assetLock( - moduleName, ASSET_LOADER_TYPE_SCRIPT, &input - ); - - errorret_t err = assetRequireLoaded(entry); - if(errorIsNotOk(err)) { - assetUnlockEntry(entry); - return moduleBaseThrowError(err); - } - - if(jerry_value_is_undefined(entry->data.script.exports)) { - assetUnlockEntry(entry); - return jerry_undefined(); - } - - jerry_value_t exportsCopy = jerry_value_copy(entry->data.script.exports); - assetUnlockEntry(entry); - return exportsCopy; -} - -jerry_value_t moduleRequireAsyncFunc( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc -) { - assertNotNull(callInfo, "callInfo must not be null."); - assertNotNull(args, "args must not be null."); - - if(argc < 1 || !jerry_value_is_string(args[0])) { - return moduleBaseThrow("requireAsync expects a filename string."); - } - if( - jerry_string_size(args[0], JERRY_ENCODING_UTF8) >= ASSET_FILE_NAME_MAX - ) { - return moduleBaseThrow("Module name too long."); - } - if(MODULE_REQUIRE_ASYNC_PEND_COUNT >= MODULE_REQUIRE_ASYNC_MAX) { - return moduleBaseThrow("Too many pending requireAsync calls."); - } - - char_t moduleName[ASSET_FILE_NAME_MAX]; - moduleBaseToString(args[0], moduleName, sizeof(moduleName)); - - assetloaderinput_t input; - input.script.nothing = NULL; - assetentry_t *entry = assetLock( - moduleName, ASSET_LOADER_TYPE_SCRIPT, &input - ); - - jerry_value_t promise = jerry_promise(); - - if(entry->state == ASSET_ENTRY_STATE_LOADED) { - jerry_value_t exports = ( - jerry_value_is_undefined(entry->data.script.exports) ? - jerry_undefined() : - jerry_value_copy(entry->data.script.exports) - ); - assetUnlockEntry(entry); - jerry_value_t ret = jerry_promise_resolve(promise, exports); - jerry_value_free(ret); - jerry_value_free(exports); - return promise; - } - - if(entry->state == ASSET_ENTRY_STATE_ERROR) { - assetUnlockEntry(entry); - jerry_value_t errStr = jerry_string_sz("Module load failed"); - jerry_value_t ret = jerry_promise_reject(promise, errStr); - jerry_value_free(ret); - jerry_value_free(errStr); - return promise; - } - - if(!scriptPromisePendHas( - MODULE_REQUIRE_ASYNC_PEND, MODULE_REQUIRE_ASYNC_PEND_COUNT, entry - )) { - if(entry->onLoaded.count >= entry->onLoaded.size) { - assetUnlockEntry(entry); - jerry_value_free(promise); - return moduleBaseThrow( - "requireAsync: onLoaded event capacity exceeded." - ); - } - if(entry->onError.count >= entry->onError.size) { - assetUnlockEntry(entry); - jerry_value_free(promise); - return moduleBaseThrow( - "requireAsync: onError event capacity exceeded." - ); - } - eventSubscribe(&entry->onLoaded, moduleRequireAsyncOnLoaded, entry); - eventSubscribe(&entry->onError, moduleRequireAsyncOnError, entry); - } - - scriptPromisePendAdd( - MODULE_REQUIRE_ASYNC_PEND, &MODULE_REQUIRE_ASYNC_PEND_COUNT, - MODULE_REQUIRE_ASYNC_MAX, entry, promise - ); - return promise; -} - -void moduleRequireInit(void) { - MODULE_REQUIRE_ASYNC_PEND_COUNT = 0; - moduleBaseDefineGlobalMethod("require", moduleRequireFunc); - moduleBaseDefineGlobalMethod("requireAsync", moduleRequireAsyncFunc); -} - -void moduleRequireDispose(void) { - for(uint32_t i = 0; i < MODULE_REQUIRE_ASYNC_PEND_COUNT; i++) { - assetentry_t *e = (assetentry_t *)MODULE_REQUIRE_ASYNC_PEND[i].key; - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(MODULE_REQUIRE_ASYNC_PEND[j].key == e) { - seen = true; break; - } - } - if(!seen) { - eventUnsubscribe(&e->onLoaded, moduleRequireAsyncOnLoaded); - eventUnsubscribe(&e->onError, moduleRequireAsyncOnError); - } - assetUnlockEntry(e); - } - scriptPromisePendFreeAll( - MODULE_REQUIRE_ASYNC_PEND, &MODULE_REQUIRE_ASYNC_PEND_COUNT - ); -} diff --git a/src/dusk/script/module/require/modulerequire.h b/src/dusk/script/module/require/modulerequire.h deleted file mode 100644 index 4bf5a576..00000000 --- a/src/dusk/script/module/require/modulerequire.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "asset/asset.h" - -/** - * Success loaded callback for async module loading. - * - * @param params The asset entry that finished loading. - * @param user Unused. - */ -void moduleRequireAsyncOnLoaded(void *params, void *user); - -/** - * Error callback for when an async loaded module fails. - * - * @param params The asset entry that failed to load. - * @param user Unused. - */ -void moduleRequireAsyncOnError(void *params, void *user); - -/** - * Synchronous require function exposed to scripts. - * - * @param callInfo JerryScript call info. - * @param args The arguments passed to the function. - * @param argc The number of arguments passed. - * @return The exports of the required module. - */ -jerry_value_t moduleRequireFunc( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc -); - -/** - * Asynchronous require function exposed to scripts. - * - * @param callInfo JerryScript call info. - * @param args The arguments passed to the function. - * @param argc The number of arguments passed. - * @return A promise that resolves with the exports of the required module. - */ -jerry_value_t moduleRequireAsyncFunc( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc -); - -/** - * Initializes the require module. - */ -void moduleRequireInit(void); - -/** - * Disposes the require module. - */ -void moduleRequireDispose(void); \ No newline at end of file diff --git a/src/dusk/script/module/save/CMakeLists.txt b/src/dusk/script/module/save/CMakeLists.txt deleted file mode 100644 index 9121bce6..00000000 --- a/src/dusk/script/module/save/CMakeLists.txt +++ /dev/null @@ -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 - modulesave.c -) diff --git a/src/dusk/script/module/save/modulesave.c b/src/dusk/script/module/save/modulesave.c deleted file mode 100644 index 4ab0e8ca..00000000 --- a/src/dusk/script/module/save/modulesave.c +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulesave.h" - -scriptproto_t MODULE_SAVE_PROTO; - -moduleBaseFunction(moduleSaveExists) { - moduleBaseRequireArgs(1); - moduleSaveRequireSlot(0, "Save.exists"); - return jerry_boolean(saveExists((uint8_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleSaveLoad) { - moduleBaseRequireArgs(1); - moduleSaveRequireSlot(0, "Save.load"); - errorret_t err = saveLoad((uint8_t)moduleBaseArgInt(0)); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleSaveWrite) { - moduleBaseRequireArgs(1); - moduleSaveRequireSlot(0, "Save.write"); - errorret_t err = saveWrite((uint8_t)moduleBaseArgInt(0)); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleSaveDelete) { - moduleBaseRequireArgs(1); - moduleSaveRequireSlot(0, "Save.delete"); - errorret_t err = saveDelete((uint8_t)moduleBaseArgInt(0)); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -void moduleSaveInit(void) { - scriptProtoInit(&MODULE_SAVE_PROTO, "Save", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticFunc( - &MODULE_SAVE_PROTO, "exists", moduleSaveExists - ); - scriptProtoDefineStaticFunc( - &MODULE_SAVE_PROTO, "load", moduleSaveLoad - ); - scriptProtoDefineStaticFunc( - &MODULE_SAVE_PROTO, "write", moduleSaveWrite - ); - scriptProtoDefineStaticFunc( - &MODULE_SAVE_PROTO, "delete", moduleSaveDelete - ); -} - -void moduleSaveDispose(void) { - scriptProtoDispose(&MODULE_SAVE_PROTO); -} diff --git a/src/dusk/script/module/save/modulesave.h b/src/dusk/script/module/save/modulesave.h deleted file mode 100644 index 56d913d3..00000000 --- a/src/dusk/script/module/save/modulesave.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "save/save.h" - -extern scriptproto_t MODULE_SAVE_PROTO; - -/** - * Validates a save-slot argument and returns a type error if invalid. - * - * @param i Argument index. - * @param ctx Context string for error messages. - */ -#define moduleSaveRequireSlot(i, ctx) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow(ctx ": slot must be a number"); \ - } \ - const int32_t _s = moduleBaseArgInt(i); \ - if(_s < 0 || _s >= (int32_t)SAVE_FILE_COUNT_MAX) { \ - return moduleBaseThrow(ctx ": invalid save slot"); \ - } \ -} while(0) - -/** - * Save.exists(slot) - true if a save file is present for the slot. - * - * @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1). - */ -moduleBaseFunction(moduleSaveExists); - -/** - * Save.load(slot) - loads the save file for the given slot. - * - * @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1). - */ -moduleBaseFunction(moduleSaveLoad); - -/** - * Save.write(slot) - writes the save file for the given slot. - * - * @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1). - */ -moduleBaseFunction(moduleSaveWrite); - -/** - * Save.delete(slot) - deletes the save file for the given slot. - * - * @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1). - */ -moduleBaseFunction(moduleSaveDelete); - -/** - * Initializes the Save module and registers Save.exists/load/write/delete. - */ -void moduleSaveInit(void); - -/** - * Disposes the Save module. - */ -void moduleSaveDispose(void); diff --git a/src/dusk/script/module/scene/CMakeLists.txt b/src/dusk/script/module/scene/CMakeLists.txt deleted file mode 100644 index 4ef416e3..00000000 --- a/src/dusk/script/module/scene/CMakeLists.txt +++ /dev/null @@ -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 - modulescene.c -) diff --git a/src/dusk/script/module/scene/modulescene.c b/src/dusk/script/module/scene/modulescene.c deleted file mode 100644 index bfe70383..00000000 --- a/src/dusk/script/module/scene/modulescene.c +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulescene.h" - -scriptproto_t MODULE_SCENE_PROTO; - -void moduleSceneInit(void) { - scriptProtoInit(&MODULE_SCENE_PROTO, "Scene", sizeof(uint8_t), NULL); -} - -void moduleSceneDispose(void) { - scriptProtoDispose(&MODULE_SCENE_PROTO); -} diff --git a/src/dusk/script/module/scene/modulescene.h b/src/dusk/script/module/scene/modulescene.h deleted file mode 100644 index 898a3c20..00000000 --- a/src/dusk/script/module/scene/modulescene.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/module/asset/moduleassetbatch.h" -#include "script/scriptproto.h" -#include "scene/scene.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "util/memory.h" - -extern scriptproto_t MODULE_SCENE_PROTO; - -/** - * Initializes the Scene module and registers the global Scene object. - */ -void moduleSceneInit(void); - -/** - * Disposes the Scene module. - */ -void moduleSceneDispose(void); diff --git a/src/dusk/script/module/story/CMakeLists.txt b/src/dusk/script/module/story/CMakeLists.txt deleted file mode 100644 index 50da105d..00000000 --- a/src/dusk/script/module/story/CMakeLists.txt +++ /dev/null @@ -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 - modulestory.c -) diff --git a/src/dusk/script/module/story/modulestory.c b/src/dusk/script/module/story/modulestory.c deleted file mode 100644 index c87d1371..00000000 --- a/src/dusk/script/module/story/modulestory.c +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulestory.h" -#include - -scriptproto_t MODULE_STORY_PROTO; - -moduleBaseFunction(moduleStoryGet) { - moduleBaseRequireArgs(1); - moduleStoryRequireFlag(0, "Story.get"); - const storyflag_t flag = (storyflag_t)moduleBaseArgInt(0); - return jerry_number((double)storyFlagGet(flag)); -} - -moduleBaseFunction(moduleStorySet) { - moduleBaseRequireArgs(2); - moduleStoryRequireFlag(0, "Story.set"); - moduleBaseRequireNumber(1); - const int32_t val = moduleBaseArgInt(1); - if(val < 0 || val > 255) { - return moduleBaseThrow("Story.set: value out of range (0-255)"); - } - storyFlagSet( - (storyflag_t)moduleBaseArgInt(0), - (storyflagvalue_t)val - ); - return jerry_undefined(); -} - -void moduleStoryInit(void) { - scriptProtoInit(&MODULE_STORY_PROTO, "Story", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticFunc( - &MODULE_STORY_PROTO, "get", moduleStoryGet - ); - scriptProtoDefineStaticFunc( - &MODULE_STORY_PROTO, "set", moduleStorySet - ); - - jerry_value_t result = jerry_eval( - (const jerry_char_t *)STORY_FLAG_SCRIPT, - strlen(STORY_FLAG_SCRIPT), - JERRY_PARSE_NO_OPTS - ); - jerry_value_free(result); -} - -void moduleStoryDispose(void) { - scriptProtoDispose(&MODULE_STORY_PROTO); -} diff --git a/src/dusk/script/module/story/modulestory.h b/src/dusk/script/module/story/modulestory.h deleted file mode 100644 index d5ab6a5b..00000000 --- a/src/dusk/script/module/story/modulestory.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "story/storyflag.h" - -extern scriptproto_t MODULE_STORY_PROTO; - -/** - * Validates a storyflag_t argument and returns a type error if invalid. - * - * @param i Argument index. - * @param ctx Context string for error messages. - */ -#define moduleStoryRequireFlag(i, ctx) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow(ctx ": flagId must be a number"); \ - } \ - const storyflag_t _f = (storyflag_t)moduleBaseArgInt(i); \ - if(_f <= STORY_FLAG_NULL || _f >= STORY_FLAG_COUNT) { \ - return moduleBaseThrow(ctx ": invalid story flag ID"); \ - } \ -} while(0) - -/** - * Story.get(flagId) - returns the current uint8 value of a story flag. - * - * @param args[0] Story flag constant (STORY_FLAG_*). - */ -moduleBaseFunction(moduleStoryGet); - -/** - * Story.set(flagId, value) - sets a story flag to a uint8 value. - * - * @param args[0] Story flag constant (STORY_FLAG_*). - * @param args[1] Value 0-255. - */ -moduleBaseFunction(moduleStorySet); - -/** - * Initializes the Story module, registers Story.get/set, and evaluates - * STORY_FLAG_SCRIPT to inject STORY_FLAG_* constants as globals. - */ -void moduleStoryInit(void); - -/** - * Disposes the Story module. - */ -void moduleStoryDispose(void); diff --git a/src/dusk/script/module/system/CMakeLists.txt b/src/dusk/script/module/system/CMakeLists.txt deleted file mode 100644 index b7f82eb8..00000000 --- a/src/dusk/script/module/system/CMakeLists.txt +++ /dev/null @@ -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 - modulesystem.c -) diff --git a/src/dusk/script/module/system/modulesystem.c b/src/dusk/script/module/system/modulesystem.c deleted file mode 100644 index 356c7aea..00000000 --- a/src/dusk/script/module/system/modulesystem.c +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulesystem.h" - -scriptproto_t MODULE_SYSTEM_PROTO; - -moduleBaseFunction(moduleSystemGetPlatform) { - return jerry_number((double)systemGetPlatform()); -} - -void moduleSystemInit(void) { - scriptProtoInit(&MODULE_SYSTEM_PROTO, "System", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticProp( - &MODULE_SYSTEM_PROTO, "platform", - moduleSystemGetPlatform, NULL - ); - - jerry_value_t target = MODULE_SYSTEM_PROTO.prototype; -#define SYSTEM_PLATFORM(name, value) \ - do { \ - jerry_value_t key = jerry_string_sz("PLATFORM_" #name); \ - jerry_value_t val = jerry_number((double)(value)); \ - jerry_object_set(target, key, val); \ - jerry_value_free(val); \ - jerry_value_free(key); \ - } while(0); - SYSTEM_PLATFORM_LIST -#undef SYSTEM_PLATFORM -} - -void moduleSystemDispose(void) { - scriptProtoDispose(&MODULE_SYSTEM_PROTO); -} diff --git a/src/dusk/script/module/system/modulesystem.h b/src/dusk/script/module/system/modulesystem.h deleted file mode 100644 index 65d9da98..00000000 --- a/src/dusk/script/module/system/modulesystem.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "system/system.h" - -extern scriptproto_t MODULE_SYSTEM_PROTO; - -/** - * @return Current platform as a System.PLATFORM_* constant. - */ -moduleBaseFunction(moduleSystemGetPlatform); - -/** - * Initializes the System module, registers the global System object with the - * platform property and all PLATFORM_* constants. - */ -void moduleSystemInit(void); - -/** - * Disposes the System module. - */ -void moduleSystemDispose(void); diff --git a/src/dusk/script/module/ui/CMakeLists.txt b/src/dusk/script/module/ui/CMakeLists.txt deleted file mode 100644 index d2884254..00000000 --- a/src/dusk/script/module/ui/CMakeLists.txt +++ /dev/null @@ -1,10 +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 - modulefullbox.c - moduletextbox.c -) diff --git a/src/dusk/script/module/ui/modulefullbox.c b/src/dusk/script/module/ui/modulefullbox.c deleted file mode 100644 index 85d15bd1..00000000 --- a/src/dusk/script/module/ui/modulefullbox.c +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulefullbox.h" -#include "script/scriptpromisepend.h" -#include "script/module/display/modulecolor.h" -#include "animation/easing.h" - -#define MODULE_FULLBOX_PEND_MAX 8 - -typedef struct { - uifullbox_t *fullbox; -} jsuifullbox_t; - -scriptproto_t MODULE_FULLBOX_PROTO; - -static scriptpromisepend_t MODULE_FULLBOX_PEND[MODULE_FULLBOX_PEND_MAX]; -static uint32_t MODULE_FULLBOX_PEND_COUNT = 0; - -moduleBaseFunction(moduleFullboxSetColor) { - moduleBaseRequireArgs(1); - jsuifullbox_t *d = (jsuifullbox_t *)scriptProtoGetValue( - &MODULE_FULLBOX_PROTO, callInfo->this_value - ); - if(!d || !d->fullbox) return jerry_undefined(); - color_t *c = moduleColorFrom(args[0]); - if(!c) return jerry_undefined(); - moduleFullboxTransitionFire(NULL, d->fullbox); - uiFullboxTransition(d->fullbox, *c, *c, 0.0f, EASING_LINEAR); - jerry_value_t promise = jerry_promise(); - jerry_value_t undef = jerry_undefined(); - jerry_value_t r = jerry_promise_resolve(promise, undef); - jerry_value_free(undef); - jerry_value_free(r); - return promise; -} - -moduleBaseFunction(moduleFullboxTransition) { - moduleBaseRequireArgs(3); - jsuifullbox_t *d = (jsuifullbox_t *)scriptProtoGetValue( - &MODULE_FULLBOX_PROTO, callInfo->this_value - ); - if(!d || !d->fullbox) return jerry_undefined(); - color_t *from = moduleColorFrom(args[0]); - color_t *to = moduleColorFrom(args[1]); - if(!from || !to) return jerry_undefined(); - float_t duration = moduleBaseArgFloat(2); - easingtype_t easing = (easingtype_t)moduleBaseOptInt( - 3, (int32_t)EASING_LINEAR - ); - moduleFullboxTransitionFire(NULL, d->fullbox); - uiFullboxTransition(d->fullbox, *from, *to, duration, easing); - jerry_value_t promise = jerry_promise(); - if(duration <= 0.0f) { - jerry_value_t undef = jerry_undefined(); - jerry_value_t r = jerry_promise_resolve(promise, undef); - jerry_value_free(undef); - jerry_value_free(r); - return promise; - } - if(MODULE_FULLBOX_PEND_COUNT >= MODULE_FULLBOX_PEND_MAX) { - jerry_value_t errStr = jerry_string_sz( - "UIFullbox.transition: too many pending" - ); - jerry_value_t r = jerry_promise_reject(promise, errStr); - jerry_value_free(errStr); - jerry_value_free(r); - return promise; - } - if(!scriptPromisePendHas( - MODULE_FULLBOX_PEND, MODULE_FULLBOX_PEND_COUNT, d->fullbox - )) { - eventSubscribe( - &d->fullbox->onTransitionEnd, - moduleFullboxTransitionFire, - d->fullbox - ); - } - scriptPromisePendAdd( - MODULE_FULLBOX_PEND, &MODULE_FULLBOX_PEND_COUNT, - MODULE_FULLBOX_PEND_MAX, d->fullbox, promise - ); - return promise; -} - -void moduleFullboxTransitionFire(void *params, void *user) { - uifullbox_t *fullbox = (uifullbox_t *)user; - jerry_value_t undef = jerry_undefined(); - uint32_t n = scriptPromisePendResolve( - MODULE_FULLBOX_PEND, &MODULE_FULLBOX_PEND_COUNT, fullbox, undef - ); - jerry_value_free(undef); - if(!n) return; - eventUnsubscribe( - &fullbox->onTransitionEnd, moduleFullboxTransitionFire - ); -} - -void moduleFullboxInit(void) { - MODULE_FULLBOX_PEND_COUNT = 0; - scriptProtoInit( - &MODULE_FULLBOX_PROTO, NULL, sizeof(jsuifullbox_t), NULL - ); - - scriptProtoDefineFunc( - &MODULE_FULLBOX_PROTO, "setColor", moduleFullboxSetColor - ); - scriptProtoDefineFunc( - &MODULE_FULLBOX_PROTO, "transition", moduleFullboxTransition - ); - - jsuifullbox_t over = { .fullbox = &UI_FULLBOX_OVER }; - jerry_value_t overVal = scriptProtoCreateValue( - &MODULE_FULLBOX_PROTO, &over - ); - moduleBaseSetValue("UIFullboxOver", overVal); - jerry_value_free(overVal); - - jsuifullbox_t under = { .fullbox = &UI_FULLBOX_UNDER }; - jerry_value_t underVal = scriptProtoCreateValue( - &MODULE_FULLBOX_PROTO, &under - ); - moduleBaseSetValue("UIFullboxUnder", underVal); - jerry_value_free(underVal); -} - -void moduleFullboxDispose(void) { - for(uint32_t i = 0; i < MODULE_FULLBOX_PEND_COUNT; i++) { - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(MODULE_FULLBOX_PEND[j].key == MODULE_FULLBOX_PEND[i].key) { - seen = true; break; - } - } - if(!seen) { - uifullbox_t *fb = (uifullbox_t *)MODULE_FULLBOX_PEND[i].key; - eventUnsubscribe( - &fb->onTransitionEnd, moduleFullboxTransitionFire - ); - } - } - scriptPromisePendFreeAll( - MODULE_FULLBOX_PEND, &MODULE_FULLBOX_PEND_COUNT - ); - scriptProtoDispose(&MODULE_FULLBOX_PROTO); -} diff --git a/src/dusk/script/module/ui/modulefullbox.h b/src/dusk/script/module/ui/modulefullbox.h deleted file mode 100644 index dbd320c9..00000000 --- a/src/dusk/script/module/ui/modulefullbox.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "ui/uifullbox.h" - -extern scriptproto_t MODULE_FULLBOX_PROTO; - -/** - * Event trampoline that resolves all pending transition promises for a - * fullbox and unsubscribes from onTransitionEnd. Also called directly - * to flush promises when a new transition interrupts a running one. - * - * @param params Unused (receives the uifullbox_t from eventInvoke). - * @param user The uifullbox_t pointer whose transition completed. - */ -void moduleFullboxTransitionFire(void *params, void *user); - -/** - * setColor(color) - instantly sets the fullbox to a solid color. - * Returns a Promise that resolves immediately. - * - * @param args[0] Color instance. - */ -moduleBaseFunction(moduleFullboxSetColor); - -/** - * transition(from, to, duration, easing?) - animates the fullbox from - * one color to another. Returns a Promise that resolves when the - * transition completes. - * - * @param args[0] Starting Color. - * @param args[1] Ending Color. - * @param args[2] Duration in seconds. - * @param args[3] Optional EASING_* constant (default: EASING_LINEAR). - */ -moduleBaseFunction(moduleFullboxTransition); - -/** - * Initializes the UIFullbox module, registering UIFullboxOver and - * UIFullboxUnder as global singleton objects. - */ -void moduleFullboxInit(void); - -/** - * Disposes the UIFullbox module. - */ -void moduleFullboxDispose(void); diff --git a/src/dusk/script/module/ui/moduletextbox.c b/src/dusk/script/module/ui/moduletextbox.c deleted file mode 100644 index 459dd862..00000000 --- a/src/dusk/script/module/ui/moduletextbox.c +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletextbox.h" -#include "ui/uitextbox.h" -#include "input/inputaction.h" - -scriptproto_t MODULE_TEXTBOX_PROTO; - -moduleBaseFunction(moduleTextboxSetText) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - char_t buf[UI_TEXTBOX_TEXT_MAX]; - moduleBaseToString(args[0], buf, sizeof(buf)); - uiTextboxSetText(buf); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxNextPage) { - uiTextboxNextPage(); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxUpdate) { - errorret_t err = uiTextboxUpdate(); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxDraw) { - errorret_t err = uiTextboxDraw(); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxSetAdvanceAction) { - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - const inputaction_t action = (inputaction_t)moduleBaseArgInt(0); - if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - return moduleBaseThrow( - "UITextbox.setAdvanceAction: invalid action" - ); - } - UI_TEXTBOX.advanceAction = action; - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxGetIsPageComplete) { - return jerry_boolean(uiTextboxPageIsComplete()); -} - -moduleBaseFunction(moduleTextboxGetHasNextPage) { - return jerry_boolean(uiTextboxHasNextPage()); -} - -moduleBaseFunction(moduleTextboxGetCurrentPage) { - return jerry_number((double)UI_TEXTBOX.currentPage); -} - -moduleBaseFunction(moduleTextboxGetPageCount) { - return jerry_number((double)UI_TEXTBOX.pageCount); -} - -void moduleTextboxInit(void) { - scriptProtoInit( - &MODULE_TEXTBOX_PROTO, "UITextbox", sizeof(uint8_t), NULL - ); - - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "setText", moduleTextboxSetText - ); - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "nextPage", moduleTextboxNextPage - ); - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "update", moduleTextboxUpdate - ); - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "draw", moduleTextboxDraw - ); - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "setAdvanceAction", - moduleTextboxSetAdvanceAction - ); - scriptProtoDefineStaticProp( - &MODULE_TEXTBOX_PROTO, "isPageComplete", - moduleTextboxGetIsPageComplete, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_TEXTBOX_PROTO, "hasNextPage", - moduleTextboxGetHasNextPage, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_TEXTBOX_PROTO, "currentPage", - moduleTextboxGetCurrentPage, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_TEXTBOX_PROTO, "pageCount", - moduleTextboxGetPageCount, NULL - ); -} - -void moduleTextboxDispose(void) { - scriptProtoDispose(&MODULE_TEXTBOX_PROTO); -} diff --git a/src/dusk/script/module/ui/moduletextbox.h b/src/dusk/script/module/ui/moduletextbox.h deleted file mode 100644 index ace7e0ab..00000000 --- a/src/dusk/script/module/ui/moduletextbox.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" - -extern scriptproto_t MODULE_TEXTBOX_PROTO; - -/** - * UITextbox.setText(text) - sets the textbox content and rebuilds layout. - * - * @param args[0] Null-terminated text string. - */ -moduleBaseFunction(moduleTextboxSetText); - -/** - * UITextbox.nextPage() - advances to the next page; no-op on last page. - */ -moduleBaseFunction(moduleTextboxNextPage); - -/** - * UITextbox.update() - advances the typewriter scroll by one tick. - */ -moduleBaseFunction(moduleTextboxUpdate); - -/** - * UITextbox.draw() - draws the frame and visible text for this frame. - */ -moduleBaseFunction(moduleTextboxDraw); - -/** - * UITextbox.setAdvanceAction(action) - sets the input action that - * advances the textbox when held. - * - * @param args[0] Input action constant (INPUT_ACTION_*). - */ -moduleBaseFunction(moduleTextboxSetAdvanceAction); - -/** @return true when the typewriter has fully revealed the current page. */ -moduleBaseFunction(moduleTextboxGetIsPageComplete); - -/** @return true when at least one more page follows the current one. */ -moduleBaseFunction(moduleTextboxGetHasNextPage); - -/** @return Zero-based index of the current page. */ -moduleBaseFunction(moduleTextboxGetCurrentPage); - -/** @return Total number of pages for the current text. */ -moduleBaseFunction(moduleTextboxGetPageCount); - -/** - * Initializes the UITextbox module and registers all methods and - * properties on the UITextbox global. - */ -void moduleTextboxInit(void); - -/** - * Disposes the UITextbox module. - */ -void moduleTextboxDispose(void); diff --git a/src/dusk/script/script.c b/src/dusk/script/script.c deleted file mode 100644 index 043c538e..00000000 --- a/src/dusk/script/script.c +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "script.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "asset/loader/assetentry.h" -#include "script/module/modulelist.h" -#include - -script_t SCRIPT; - -static void scriptExceptionInfo( - jerry_value_t exception, - char_t *msgBuf, size_t msgSize, - char_t *stackBuf, size_t stackSize -) { - jerry_value_t errVal = jerry_exception_value(exception, false); - - jerry_value_t errStr = jerry_value_to_string(errVal); - jerry_size_t len = jerry_string_to_buffer( - errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)msgBuf, msgSize - 1 - ); - msgBuf[len] = '\0'; - jerry_value_free(errStr); - - stackBuf[0] = '\0'; - jerry_value_t stackKey = jerry_string_sz("stack"); - jerry_value_t stackVal = jerry_object_get(errVal, stackKey); - jerry_value_free(stackKey); - if( - !jerry_value_is_exception(stackVal) && - !jerry_value_is_undefined(stackVal) - ) { - jerry_value_t stackStr = jerry_value_to_string(stackVal); - jerry_size_t stackLen = jerry_string_to_buffer( - stackStr, JERRY_ENCODING_UTF8, (jerry_char_t *)stackBuf, stackSize - 1 - ); - stackBuf[stackLen] = '\0'; - jerry_value_free(stackStr); - } - jerry_value_free(stackVal); - jerry_value_free(errVal); -} - -errorret_t scriptInit(void) { - memoryZero(&SCRIPT, sizeof(script_t)); - - jerry_init(JERRY_INIT_EMPTY); - SCRIPT.initialized = true; - - moduleListInit(); - - errorOk(); -} - -errorret_t scriptUpdate() { - moduleListUpdate(); - jerry_value_t ret = jerry_run_jobs(); - - if(jerry_value_is_exception(ret)) { - char_t buf[256]; - char_t stack[512]; - scriptExceptionInfo(ret, buf, sizeof(buf), stack, sizeof(stack)); - jerry_value_free(ret); - if(stack[0]) errorThrow("Script error: %s\n%s", buf, stack); - errorThrow("Script error: %s", buf); - } - - jerry_value_free(ret); - errorOk(); -} - -errorret_t scriptExecString(const char_t *source) { - assertNotNull(source, "Source cannot be NULL"); - assertTrue(SCRIPT.initialized, "Script system not initialized"); - - jerry_value_t result = jerry_eval( - (const jerry_char_t *)source, - strlen(source), - JERRY_PARSE_NO_OPTS - ); - - if(jerry_value_is_exception(result)) { - char_t buf[256]; - char_t stack[512]; - scriptExceptionInfo(result, buf, sizeof(buf), stack, sizeof(stack)); - jerry_value_free(result); - if(stack[0]) errorThrow("Script error: %s\n%s", buf, stack); - errorThrow("Script error: %s", buf); - } - - jerry_value_free(result); - errorOk(); -} - -errorret_t scriptExecFile(const char_t *path) { - assertNotNull(path, "Path cannot be NULL"); - assertTrue(SCRIPT.initialized, "Script system not initialized"); - - assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_SCRIPT, NULL); - assertNotNull(entry, "Failed to get asset entry for script"); - errorChain(assetRequireLoaded(entry)); - assetUnlockEntry(entry); - - errorOk(); -} - -errorret_t scriptDispose(void) { - if(!SCRIPT.initialized) errorOk(); - - // Make a long story short we need to dispose script assets here, because the - // asset reaper isn't called until later. - assetentry_t *entries[ASSET_ENTRY_COUNT_MAX]; - uint32_t count = assetGetEntriesOfType(entries, ASSET_LOADER_TYPE_SCRIPT); - - // Release the locks - for(size_t i = 0; i < count; i++) { - assetUnlockEntry(entries[i]); - assetRequireDisposed(entries[i]); - } - - assertTrue( - assetGetEntriesOfType(entries, ASSET_LOADER_TYPE_SCRIPT) == 0, - "All script assets should be disposed by now." - ); - - // Script modules are now freed, so any JS objects that were only reachable - // through module-scope globals (texEntry, renderable._tex, etc.) are now - // orphaned. A GC pass here fires their finalizers and releases asset entry - // locks before assetDispose() checks ref counts. - jerry_heap_gc(JERRY_GC_PRESSURE_HIGH); - - moduleListDispose(); - jerry_cleanup(); - SCRIPT.initialized = false; - - errorOk(); -} diff --git a/src/dusk/script/script.h b/src/dusk/script/script.h deleted file mode 100644 index 99a8e5df..00000000 --- a/src/dusk/script/script.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -typedef struct { - bool_t initialized; -} script_t; - -extern script_t SCRIPT; - -/** - * Initializes the script system, starting up JerryScript and registering all - * built-in modules. - * - * @return Any error that occurred. - */ -errorret_t scriptInit(void); - -/** - * Updates the script system, running any pending jobs. This should be called - * once per frame. - * - * @return Any error that occurred. - */ -errorret_t scriptUpdate(void); - -/** - * Evaluates a JS source string in the global scope. - * - * @param source Null-terminated JS source to evaluate. - * @return Any error that occurred. - */ -errorret_t scriptExecString(const char_t *source); - -/** - * Loads and evaluates a script asset from the archive. The result is cached - * by the asset system; repeated calls with the same path do not re-execute. - * - * @param path Path of the script inside the asset archive. - * @return Any error that occurred. - */ -errorret_t scriptExecFile(const char_t *path); - -/** - * Disposes of the script system and shuts down JerryScript. - * - * @return Any error that occurred. - */ -errorret_t scriptDispose(void); diff --git a/src/dusk/script/scriptpromisepend.c b/src/dusk/script/scriptpromisepend.c deleted file mode 100644 index 0559ca51..00000000 --- a/src/dusk/script/scriptpromisepend.c +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "scriptpromisepend.h" -#include "assert/assert.h" - -uint32_t scriptPromisePendResolve( - scriptpromisepend_t *pend, - uint32_t *count, - const void *key, - jerry_value_t value -) { - uint32_t resolved = 0; - uint32_t i = 0; - while(i < *count) { - if(pend[i].key != key) { i++; continue; } - jerry_value_t r = jerry_promise_resolve(pend[i].promise, value); - jerry_value_free(r); - jerry_value_free(pend[i].promise); - (*count)--; - if(i < *count) pend[i] = pend[*count]; - resolved++; - } - return resolved; -} - -uint32_t scriptPromisePendReject( - scriptpromisepend_t *pend, - uint32_t *count, - const void *key, - jerry_value_t error -) { - uint32_t rejected = 0; - uint32_t i = 0; - while(i < *count) { - if(pend[i].key != key) { i++; continue; } - jerry_value_t r = jerry_promise_reject(pend[i].promise, error); - jerry_value_free(r); - jerry_value_free(pend[i].promise); - (*count)--; - if(i < *count) pend[i] = pend[*count]; - rejected++; - } - return rejected; -} - -bool_t scriptPromisePendHas( - const scriptpromisepend_t *pend, - uint32_t count, - const void *key -) { - for(uint32_t i = 0; i < count; i++) { - if(pend[i].key == key) return true; - } - return false; -} - -void scriptPromisePendAdd( - scriptpromisepend_t *pend, - uint32_t *count, - uint32_t max, - const void *key, - jerry_value_t promise -) { - assertTrue(*count < max, "scriptPromisePendAdd: capacity exceeded"); - pend[*count].key = (void *)key; - pend[*count].promise = jerry_value_copy(promise); - (*count)++; -} - -void scriptPromisePendFreeAll( - scriptpromisepend_t *pend, - uint32_t *count -) { - for(uint32_t i = 0; i < *count; i++) { - jerry_value_free(pend[i].promise); - } - *count = 0; -} diff --git a/src/dusk/script/scriptpromisepend.h b/src/dusk/script/scriptpromisepend.h deleted file mode 100644 index c6df046f..00000000 --- a/src/dusk/script/scriptpromisepend.h +++ /dev/null @@ -1,98 +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" -#include - -/** - * One pending promise keyed to a pointer. - * Declare a static array of these in each script module that needs to - * wait on a C event before resolving a JS promise. - */ -typedef struct { - void *key; - jerry_value_t promise; -} scriptpromisepend_t; - -/** - * Resolves all entries whose key matches and swap-removes them. - * Returns the number of promises resolved. - * - * @param pend The pending array. - * @param count Current count (updated in place). - * @param key Key to match. - * @param value Value passed to jerry_promise_resolve. - * @return Number of promises resolved. - */ -uint32_t scriptPromisePendResolve( - scriptpromisepend_t *pend, - uint32_t *count, - const void *key, - jerry_value_t value -); - -/** - * Rejects all entries whose key matches and swap-removes them. - * Returns the number of promises rejected. - * - * @param pend The pending array. - * @param count Current count (updated in place). - * @param key Key to match. - * @param error Reason passed to jerry_promise_reject. - * @return Number of promises rejected. - */ -uint32_t scriptPromisePendReject( - scriptpromisepend_t *pend, - uint32_t *count, - const void *key, - jerry_value_t error -); - -/** - * Returns true if any entry has key equal to key. - * - * @param pend The pending array. - * @param count Current count. - * @param key Key to search for. - * @return true if found. - */ -bool_t scriptPromisePendHas( - const scriptpromisepend_t *pend, - uint32_t count, - const void *key -); - -/** - * Appends a new entry. Stores a copy of the promise; the caller keeps - * the original to return to script. Asserts that count < max. - * - * @param pend The pending array. - * @param count Current count (incremented in place). - * @param max Capacity of the array. - * @param key Key to associate with the promise. - * @param promise Promise to copy into the array. - */ -void scriptPromisePendAdd( - scriptpromisepend_t *pend, - uint32_t *count, - uint32_t max, - const void *key, - jerry_value_t promise -); - -/** - * Frees all stored promise references without resolving them, then - * zeros count. Call during module dispose before cleaning up events. - * - * @param pend The pending array. - * @param count Current count (set to 0). - */ -void scriptPromisePendFreeAll( - scriptpromisepend_t *pend, - uint32_t *count -); diff --git a/src/dusk/script/scriptproto.c b/src/dusk/script/scriptproto.c deleted file mode 100644 index c88f0a57..00000000 --- a/src/dusk/script/scriptproto.c +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "scriptproto.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "script/module/modulebase.h" - -void scriptProtoInit( - scriptproto_t *proto, - const char_t *name, - const size_t size, - jerry_external_handler_t constructor -) { - assertNotNull(proto, "Script prototype struct must not be null"); - memoryZero(proto, sizeof(scriptproto_t)); - - proto->info = (jerry_object_native_info_t){ - .free_cb = moduleBaseFreeProto, - .number_of_references = 0, - .offset_of_references = 0 - }; - proto->prototype = jerry_object(); - proto->size = size; - - if(constructor != NULL) { - proto->constructor = jerry_function_external(constructor); - jerry_value_t protoKey = jerry_string_sz("prototype"); - jerry_object_set(proto->constructor, protoKey, proto->prototype); - jerry_value_free(protoKey); - jerry_value_t ctorKey = jerry_string_sz("constructor"); - jerry_object_set(proto->prototype, ctorKey, proto->constructor); - jerry_value_free(ctorKey); - } - - if(name != NULL) { - jerry_value_t global = jerry_current_realm(); - jerry_value_t key = jerry_string_sz(name); - jerry_value_t val = proto->constructor - ? proto->constructor - : proto->prototype; - jerry_object_set(global, key, val); - jerry_value_free(key); - jerry_value_free(global); - } -} - -void scriptProtoDefineProp( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t getter, - jerry_external_handler_t setter -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertStrLenMin(name, 1, "Property name must not be empty"); - assertNotNull(getter, "Getter must not be null"); - - jerry_property_descriptor_t desc; - memoryZero(&desc, sizeof(desc)); - desc.flags = (uint16_t)( - JERRY_PROP_IS_GET_DEFINED | - JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE | - JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE - ); - desc.getter = jerry_function_external(getter); - if(setter != NULL) { - desc.flags |= JERRY_PROP_IS_SET_DEFINED; - desc.setter = jerry_function_external(setter); - } - - jerry_value_t key = jerry_string_sz(name); - jerry_value_t result = jerry_object_define_own_prop( - proto->prototype, key, &desc - ); - jerry_value_free(result); - jerry_value_free(key); - jerry_value_free(desc.getter); - if(setter != NULL) jerry_value_free(desc.setter); -} - -void scriptProtoDefineFunc( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t fn -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertStrLenMin(name, 1, "Method name must not be empty"); - assertNotNull(fn, "Function handler must not be null"); - - jerry_value_t key = jerry_string_sz(name); - jerry_value_t func = jerry_function_external(fn); - jerry_object_set(proto->prototype, key, func); - jerry_value_free(func); - jerry_value_free(key); -} - -void scriptProtoDefineStaticProp( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t getter, - jerry_external_handler_t setter -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertStrLenMin(name, 1, "Property name must not be empty"); - assertNotNull(getter, "Getter must not be null"); - - jerry_value_t target = ( - proto->constructor ? proto->constructor : proto->prototype - ); - - jerry_property_descriptor_t desc; - memoryZero(&desc, sizeof(desc)); - desc.flags = (uint16_t)( - JERRY_PROP_IS_GET_DEFINED | - JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE | - JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE - ); - desc.getter = jerry_function_external(getter); - if(setter != NULL) { - desc.flags |= JERRY_PROP_IS_SET_DEFINED; - desc.setter = jerry_function_external(setter); - } - - jerry_value_t key = jerry_string_sz(name); - jerry_value_t result = jerry_object_define_own_prop(target, key, &desc); - jerry_value_free(result); - jerry_value_free(key); - jerry_value_free(desc.getter); - if(setter != NULL) jerry_value_free(desc.setter); -} - -void scriptProtoDefineStaticFunc( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t fn -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertStrLenMin(name, 1, "Method name must not be empty"); - assertNotNull(fn, "Function handler must not be null"); - - jerry_value_t target = ( - proto->constructor ? proto->constructor : proto->prototype - ); - jerry_value_t key = jerry_string_sz(name); - jerry_value_t func = jerry_function_external(fn); - jerry_object_set(target, key, func); - jerry_value_free(func); - jerry_value_free(key); -} - -jerry_value_t scriptProtoCreateValue( - const scriptproto_t *proto, - const void *value -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertNotNull(value, "Value pointer must not be null"); - - void *ptr = memoryAllocate(proto->size); - memoryCopy(ptr, value, proto->size); - jerry_value_t obj = jerry_object(); - jerry_object_set_native_ptr(obj, &proto->info, ptr); - jerry_object_set_proto(obj, proto->prototype); - return obj; -} - -void *scriptProtoGetValue( - const scriptproto_t *proto, - const jerry_value_t obj -) { - assertNotNull(proto, "Script prototype struct must not be null"); - if(!jerry_value_is_object(obj)) return NULL; - return jerry_object_get_native_ptr(obj, &proto->info); -} - -void scriptProtoDefineToString( - scriptproto_t *proto, - jerry_external_handler_t fn -) { - scriptProtoDefineFunc(proto, "toString", fn); -} - -void scriptProtoDispose(scriptproto_t *proto) { - assertNotNull(proto, "Script prototype struct must not be null"); - jerry_value_free(proto->prototype); - if(proto->constructor) jerry_value_free(proto->constructor); -} diff --git a/src/dusk/script/scriptproto.h b/src/dusk/script/scriptproto.h deleted file mode 100644 index eb11b24a..00000000 --- a/src/dusk/script/scriptproto.h +++ /dev/null @@ -1,156 +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" -#include - -/** - * Represents a JavaScript class prototype backed by a C struct. - * Use scriptProtoInit() to populate, scriptProtoDispose() to clean up. - */ -typedef struct { - /** Native-info tag used to identify instances of this class. */ - jerry_object_native_info_t info; - /** The JS prototype object shared by all instances. */ - jerry_value_t prototype; - /** The JS constructor function, or 0 if no constructor was given. */ - jerry_value_t constructor; - /** sizeof the C struct wrapped by each instance. */ - size_t size; -} scriptproto_t; - -/** - * Initializes a JS class prototype. - * - * If name is non-NULL the class is registered as a global. When ctor is - * also non-NULL the global is the constructor function (enabling - * `new Name(...)`); otherwise the prototype object itself becomes the - * global. - * - * @param proto The struct to initialize. - * @param name JS global name, or NULL to skip global registration. - * @param size sizeof the C struct this class wraps. - * @param ctor Constructor handler, or NULL for no constructor. - */ -void scriptProtoInit( - scriptproto_t *proto, - const char_t *name, - const size_t size, - jerry_external_handler_t ctor -); - -/** - * Defines an instance property with a getter and optional setter. - * - * @param proto The class prototype. - * @param name Property name. - * @param getter Getter handler (must not be NULL). - * @param setter Setter handler, or NULL for a read-only property. - */ -void scriptProtoDefineProp( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t getter, - jerry_external_handler_t setter -); - -/** - * Defines an instance method on the class prototype. - * - * @param proto The class prototype. - * @param name Method name. - * @param fn C handler called when the method is invoked. - */ -void scriptProtoDefineFunc( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t fn -); - -/** - * Defines a static property on the class (e.g. Console.visible). - * - * Attaches to the constructor when one exists, otherwise attaches - * directly to the prototype object (which is the global in that case). - * - * @param proto The class prototype. - * @param name Property name. - * @param getter Getter handler (must not be NULL). - * @param setter Setter handler, or NULL for a read-only property. - */ -void scriptProtoDefineStaticProp( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t getter, - jerry_external_handler_t setter -); - -/** - * Defines a static method on the class (e.g. Console.print). - * - * Attaches to the constructor when one exists, otherwise attaches - * directly to the prototype object. - * - * @param proto The class prototype. - * @param name Method name. - * @param fn C handler called when the static method is invoked. - */ -void scriptProtoDefineStaticFunc( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t fn -); - -/** - * Creates a JS instance wrapping a copy of a C value. - * - * Allocates a copy of the value, sets it as native data on a new JS - * object, and applies the class prototype. - * - * @param proto The class prototype. - * @param value Pointer to the C value to copy into the new JS object. - * @return A new JS object with the class prototype and native data set. - */ -jerry_value_t scriptProtoCreateValue( - const scriptproto_t *proto, - const void *value -); - -/** - * Unwraps the native C pointer from a JS object instance. - * - * @param proto The class prototype. - * @param obj The JS object to inspect. - * @return Pointer to the wrapped C value, or NULL if not an instance. - */ -void *scriptProtoGetValue( - const scriptproto_t *proto, - const jerry_value_t obj -); - -/** - * Defines a toString() method on the class prototype. - * - * @param proto The class prototype. - * @param fn C handler called when toString() is invoked on an - * instance. - */ -void scriptProtoDefineToString( - scriptproto_t *proto, - jerry_external_handler_t fn -); - -/** - * Releases all JerryScript resources held by the prototype. - * - * Must be called before jerry_cleanup() to avoid heap finalizer - * crashes from live jerry_value_t GC roots. - * - * @param proto The class prototype to dispose. - */ -void scriptProtoDispose(scriptproto_t *proto); diff --git a/tools/js2c/__main__.py b/tools/js2c/__main__.py deleted file mode 100644 index 14874501..00000000 --- a/tools/js2c/__main__.py +++ /dev/null @@ -1,53 +0,0 @@ -import argparse -import os -import re - -parser = argparse.ArgumentParser(description="Embed a JS file as a C string header") -parser.add_argument("--input", required=True, help="Path to input JS file") -parser.add_argument("--output", required=True, help="Path to output .h file") -parser.add_argument("--name", help="C identifier name (default: derived from filename)") -args = parser.parse_args() - -if args.name: - name = args.name -else: - stem = os.path.splitext(os.path.basename(args.input))[0] - name = re.sub(r"[^a-zA-Z0-9]", "_", stem).upper() + "_JS" - -with open(args.input, "r", encoding="utf-8") as f: - source = f.read() - -def escape_line(s): - s = s.replace("\\", "\\\\") - s = s.replace('"', '\\"') - s = s.replace("\r", "\\r") - s = s.replace("\t", "\\t") - return s - -lines = source.split("\n") -if lines and lines[-1] == "": - lines = lines[:-1] - -out = [ - "#pragma once", - "#include ", - "", - f"static const char {name}[] =", -] - -if not lines: - out[-1] += ' "";' -else: - for i, line in enumerate(lines): - suffix = ";" if i == len(lines) - 1 else "" - out.append(f' "{escape_line(line)}\\n"{suffix}') - -out += [ - "", - f"static const size_t {name}_SIZE = sizeof({name}) - 1;", - "", -] - -os.makedirs(os.path.dirname(args.output), exist_ok=True) -with open(args.output, "w", encoding="utf-8") as f: - f.write("\n".join(out)) diff --git a/tools/story/csv/__main__.py b/tools/story/csv/__main__.py index d3ebed93..c1f9bcfc 100644 --- a/tools/story/csv/__main__.py +++ b/tools/story/csv/__main__.py @@ -25,7 +25,7 @@ with open(args.csv, newline="", encoding="utf-8") as f: # Build output out = [ "#pragma once", - '#include "story/storyflagdefs.h"', + '#include "rpg/story/storyflagdefs.h"', "", "typedef enum {", " STORY_FLAG_NULL,",