1 Commits

Author SHA1 Message Date
YourWishes 643a8077dd Socket first pass 2026-04-19 18:53:09 -05:00
174 changed files with 4145 additions and 5904 deletions
+15 -15
View File
@@ -53,21 +53,21 @@ jobs:
path: ./git-artifcats/Dusk path: ./git-artifcats/Dusk
if-no-files-found: error if-no-files-found: error
# build-vita: build-vita:
# runs-on: ubuntu-latest runs-on: ubuntu-latest
# steps: steps:
# - name: Checkout repository - name: Checkout repository
# uses: actions/checkout@v6 uses: actions/checkout@v6
# - name: Set up Docker - name: Set up Docker
# uses: docker/setup-docker-action@v5 uses: docker/setup-docker-action@v5
# - name: Build Vita - name: Build Vita
# run: ./scripts/build-vita-docker.sh run: ./scripts/build-vita-docker.sh
# - name: Upload Vita binary - name: Upload Vita binary
# uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
# with: with:
# name: dusk-vita name: dusk-vita
# path: build-vita/Dusk.vpk path: build-vita/Dusk.vpk
# if-no-files-found: error if-no-files-found: error
build-knulli: build-knulli:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+1 -3
View File
@@ -4,13 +4,11 @@
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
# Setup # Setup
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.18)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
# [cmake] This is allowed only when policy CMP0079 is set to NEW.
cmake_policy(SET CMP0079 NEW)
option(DUSK_BUILD_TESTS "Enable tests" OFF) option(DUSK_BUILD_TESTS "Enable tests" OFF)
-22
View File
@@ -1,22 +0,0 @@
var OverworldEntity = include('entities/OverworldEntity.js');
function CubeEntity() {
OverworldEntity.call(this);
this.add(MESH);
this.add(MATERIAL);
}
CubeEntity.prototype = Object.create(OverworldEntity.prototype);
CubeEntity.prototype.constructor = CubeEntity;
CubeEntity.prototype.update = function() {
OverworldEntity.prototype.update.call(this);
var speed = 3.0;
var move = Input.axis2D(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT, INPUT_ACTION_UP, INPUT_ACTION_DOWN);
this.position.position.x += move.x * speed * TIME.delta;
this.position.position.z += move.y * speed * TIME.delta;
this.material.setColor(Color.rainbow());
};
module = CubeEntity;
-21
View File
@@ -1,21 +0,0 @@
function OverworldEntity() {
Entity.call(this);
this.add(POSITION);
}
OverworldEntity.prototype = Object.create(Entity.prototype);
OverworldEntity.prototype.constructor = OverworldEntity;
OverworldEntity.prototype.update = function() {
// var speed = 3.0;
// var move = Input.axis2D(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT, INPUT_ACTION_UP, INPUT_ACTION_DOWN);
// this.position.position.x += move.x * speed * TIME.delta;
// this.position.position.z += move.y * speed * TIME.delta;
}
OverworldEntity.prototype.dispose = function() {
// Nothing to dispose
}
module = OverworldEntity;
-77
View File
@@ -1,77 +0,0 @@
Console.visible = true;
// Default input bindings.
if (typeof PSP !== 'undefined') {
Input.bind("up", INPUT_ACTION_UP);
Input.bind("down", INPUT_ACTION_DOWN);
Input.bind("left", INPUT_ACTION_LEFT);
Input.bind("right", INPUT_ACTION_RIGHT);
Input.bind("accept", INPUT_ACTION_ACCEPT);
Input.bind("cancel", INPUT_ACTION_CANCEL);
Input.bind("select", INPUT_ACTION_RAGEQUIT);
Input.bind("lstick_up", INPUT_ACTION_UP);
Input.bind("lstick_down", INPUT_ACTION_DOWN);
Input.bind("lstick_left", INPUT_ACTION_LEFT);
Input.bind("lstick_right", INPUT_ACTION_RIGHT);
Input.bind("triangle", INPUT_ACTION_CONSOLE);
} else if (typeof DOLPHIN !== 'undefined') {
Input.bind("up", INPUT_ACTION_UP);
Input.bind("down", INPUT_ACTION_DOWN);
Input.bind("left", INPUT_ACTION_LEFT);
Input.bind("right", INPUT_ACTION_RIGHT);
Input.bind("b", INPUT_ACTION_CANCEL);
Input.bind("a", INPUT_ACTION_ACCEPT);
Input.bind("z", INPUT_ACTION_CONSOLE);
Input.bind("lstick_up", INPUT_ACTION_UP);
Input.bind("lstick_down", INPUT_ACTION_DOWN);
Input.bind("lstick_left", INPUT_ACTION_LEFT);
Input.bind("lstick_right", INPUT_ACTION_RIGHT);
} else if (typeof LINUX !== 'undefined') {
if (typeof INPUT_KEYBOARD !== 'undefined') {
Input.bind("w", INPUT_ACTION_UP);
Input.bind("s", INPUT_ACTION_DOWN);
Input.bind("a", INPUT_ACTION_LEFT);
Input.bind("d", INPUT_ACTION_RIGHT);
Input.bind("left", INPUT_ACTION_LEFT);
Input.bind("right", INPUT_ACTION_RIGHT);
Input.bind("up", INPUT_ACTION_UP);
Input.bind("down", INPUT_ACTION_DOWN);
Input.bind("enter", INPUT_ACTION_ACCEPT);
Input.bind("e", INPUT_ACTION_ACCEPT);
Input.bind("q", INPUT_ACTION_CANCEL);
Input.bind("escape", INPUT_ACTION_RAGEQUIT);
Input.bind("`", INPUT_ACTION_CONSOLE);
}
if (typeof INPUT_GAMEPAD !== 'undefined') {
Input.bind("gamepad_up", INPUT_ACTION_UP);
Input.bind("gamepad_down", INPUT_ACTION_DOWN);
Input.bind("gamepad_left", INPUT_ACTION_LEFT);
Input.bind("gamepad_right", INPUT_ACTION_RIGHT);
Input.bind("gamepad_a", INPUT_ACTION_ACCEPT);
Input.bind("gamepad_b", INPUT_ACTION_CANCEL);
Input.bind("gamepad_back", INPUT_ACTION_RAGEQUIT);
Input.bind("gamepad_lstick_up", INPUT_ACTION_UP);
Input.bind("gamepad_lstick_down", INPUT_ACTION_DOWN);
Input.bind("gamepad_lstick_left", INPUT_ACTION_LEFT);
Input.bind("gamepad_lstick_right", INPUT_ACTION_RIGHT);
}
if (typeof INPUT_POINTER !== 'undefined') {
Input.bind("mouse_x", INPUT_ACTION_POINTERX);
Input.bind("mouse_y", INPUT_ACTION_POINTERY);
}
} else {
print("Unknown platform, no default input bindings set.");
}
Scene.set('scenes/cube.js');
+76
View File
@@ -0,0 +1,76 @@
module('input')
module('platform')
module('scene')
module('locale')
-- Default Input bindings.
if PSP then
inputBind("up", INPUT_ACTION_UP)
inputBind("down", INPUT_ACTION_DOWN)
inputBind("left", INPUT_ACTION_LEFT)
inputBind("right", INPUT_ACTION_RIGHT)
inputBind("accept", INPUT_ACTION_ACCEPT)
inputBind("cancel", INPUT_ACTION_CANCEL)
inputBind("select", INPUT_ACTION_RAGEQUIT)
inputBind("lstick_up", INPUT_ACTION_UP)
inputBind("lstick_down", INPUT_ACTION_DOWN)
inputBind("lstick_left", INPUT_ACTION_LEFT)
inputBind("lstick_right", INPUT_ACTION_RIGHT)
elseif DOLPHIN then
inputBind("up", INPUT_ACTION_UP)
inputBind("down", INPUT_ACTION_DOWN)
inputBind("left", INPUT_ACTION_LEFT)
inputBind("right", INPUT_ACTION_RIGHT)
inputBind("b", INPUT_ACTION_CANCEL)
inputBind("a", INPUT_ACTION_ACCEPT)
inputBind("z", INPUT_ACTION_RAGEQUIT)
inputBind("lstick_up", INPUT_ACTION_UP)
inputBind("lstick_down", INPUT_ACTION_DOWN)
inputBind("lstick_left", INPUT_ACTION_LEFT)
inputBind("lstick_right", INPUT_ACTION_RIGHT)
elseif LINUX then
if INPUT_KEYBOARD then
inputBind("w", INPUT_ACTION_UP)
inputBind("s", INPUT_ACTION_DOWN)
inputBind("a", INPUT_ACTION_LEFT)
inputBind("d", INPUT_ACTION_RIGHT)
inputBind("left", INPUT_ACTION_LEFT)
inputBind("right", INPUT_ACTION_RIGHT)
inputBind("up", INPUT_ACTION_UP)
inputBind("down", INPUT_ACTION_DOWN)
inputBind("enter", INPUT_ACTION_ACCEPT)
inputBind("e", INPUT_ACTION_ACCEPT)
inputBind("q", INPUT_ACTION_CANCEL)
inputBind("escape", INPUT_ACTION_RAGEQUIT)
end
if INPUT_GAMEPAD then
inputBind("gamepad_up", INPUT_ACTION_UP)
inputBind("gamepad_down", INPUT_ACTION_DOWN)
inputBind("gamepad_left", INPUT_ACTION_LEFT)
inputBind("gamepad_right", INPUT_ACTION_RIGHT)
inputBind("gamepad_a", INPUT_ACTION_ACCEPT)
inputBind("gamepad_b", INPUT_ACTION_CANCEL)
inputBind("gamepad_back", INPUT_ACTION_RAGEQUIT)
inputBind("gamepad_lstick_up", INPUT_ACTION_UP)
inputBind("gamepad_lstick_down", INPUT_ACTION_DOWN)
inputBind("gamepad_lstick_left", INPUT_ACTION_LEFT)
inputBind("gamepad_lstick_right", INPUT_ACTION_RIGHT)
end
if INPUT_POINTER then
inputBind("mouse_x", INPUT_ACTION_POINTERX)
inputBind("mouse_y", INPUT_ACTION_POINTERY)
end
else
print("Unknown platform, no default input bindings set.")
end
-32
View File
@@ -1,32 +0,0 @@
var CubeEntity = include('entities/CubeEntity.js');
function CubeScene() {
this.cam = new Entity();
this.cam.add(POSITION);
this.cam.add(CAMERA);
this.cam.position.position = new Vec3(3, 3, 3);
this.cam.position.lookAt(new Vec3(0, 0, 0));
this.cube = new CubeEntity();
this.spriteEnt = new Entity();
this.spriteEnt.add(POSITION);
this.spriteEnt.position.position = new Vec3(16, 16, 0);
// this.spriteEnt.sprite.setTexture('ui/minogram.png');
}
CubeScene.prototype = Object.create(Scene.prototype);
CubeScene.prototype.constructor = CubeScene;
CubeScene.prototype.update = function() {
this.cube.update();
};
CubeScene.prototype.dispose = function() {
this.cam.dispose();
this.cube.dispose();
this.spriteEnt.dispose();
};
module = CubeScene;
-96
View File
@@ -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})
+22 -1
View File
@@ -4,7 +4,6 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_INPUT_GAMEPAD DUSK_INPUT_GAMEPAD
DUSK_DISPLAY_WIDTH=640 DUSK_DISPLAY_WIDTH=640
DUSK_DISPLAY_HEIGHT=480 DUSK_DISPLAY_HEIGHT=480
DUSK_THREAD_PTHREAD
) )
# Custom compiler flags # Custom compiler flags
@@ -22,9 +21,31 @@ set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE) set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
find_package(cglm REQUIRED) find_package(cglm REQUIRED)
# Compile lua
include(FetchContent)
FetchContent_Declare(
liblua
URL https://www.lua.org/ftp/lua-5.5.0.tar.gz
)
FetchContent_MakeAvailable(liblua)
set(LUA_SRC_DIR "${liblua_SOURCE_DIR}/src")
set(LUA_C_FILES
lapi.c lauxlib.c lbaselib.c lcode.c lcorolib.c lctype.c ldblib.c ldebug.c
ldo.c ldump.c lfunc.c lgc.c linit.c liolib.c llex.c lmathlib.c lmem.c
loadlib.c lobject.c lopcodes.c loslib.c lparser.c lstate.c lstring.c
lstrlib.c ltable.c ltablib.c ltm.c lundump.c lutf8lib.c lvm.c lzio.c
)
list(TRANSFORM LUA_C_FILES PREPEND "${LUA_SRC_DIR}/")
add_library(liblua STATIC ${LUA_C_FILES})
target_include_directories(liblua PUBLIC "${LUA_SRC_DIR}")
target_compile_definitions(liblua PRIVATE LUA_USE_C89)
add_library(lua::lua ALIAS liblua)
set(Lua_FOUND TRUE CACHE BOOL "Lua found" FORCE)
# Link libraries # Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
cglm cglm
liblua
m m
fat fat
PkgConfig::zip PkgConfig::zip
+1 -2
View File
@@ -30,7 +30,6 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_SDL2 DUSK_SDL2
DUSK_OPENGL DUSK_OPENGL
DUSK_CONSOLE_POSIX
# DUSK_OPENGL_LEGACY # DUSK_OPENGL_LEGACY
DUSK_LINUX DUSK_LINUX
DUSK_DISPLAY_SIZE_DYNAMIC DUSK_DISPLAY_SIZE_DYNAMIC
@@ -42,5 +41,5 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_INPUT_GAMEPAD DUSK_INPUT_GAMEPAD
DUSK_TIME_DYNAMIC DUSK_TIME_DYNAMIC
DUSK_NETWORK_IPV6 DUSK_NETWORK_IPV6
DUSK_THREAD_PTHREAD THREAD_PTHREAD=1
) )
+4 -21
View File
@@ -1,19 +1,6 @@
# Fixes some problems building JerryScript
set(CMAKE_AR "$ENV{PSPDEV}/bin/psp-ar" CACHE FILEPATH "" FORCE)
set(CMAKE_RANLIB "$ENV{PSPDEV}/bin/psp-ranlib" CACHE FILEPATH "" FORCE)
set(CMAKE_C_COMPILER_AR "$ENV{PSPDEV}/bin/psp-ar" CACHE FILEPATH "" FORCE)
set(CMAKE_C_COMPILER_RANLIB "$ENV{PSPDEV}/bin/psp-ranlib" CACHE FILEPATH "" FORCE)
set(CMAKE_C_ARCHIVE_CREATE "$ENV{PSPDEV}/bin/psp-ar qc <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_APPEND "$ENV{PSPDEV}/bin/psp-ar q <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_FINISH "$ENV{PSPDEV}/bin/psp-ranlib <TARGET>")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF CACHE BOOL "" FORCE)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_C OFF CACHE BOOL "" FORCE)
set(JERRY_LTO OFF CACHE BOOL "" FORCE)
find_package(jerryscript REQUIRED)
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
target_link_libraries(${DUSK_BINARY_TARGET_NAME} PUBLIC target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
${SDL2_LIBRARIES} ${SDL2_LIBRARIES}
SDL2 SDL2
pthread pthread
@@ -42,17 +29,13 @@ target_link_libraries(${DUSK_BINARY_TARGET_NAME} PUBLIC
pspnet_apctl pspnet_apctl
psphttp psphttp
pspssl pspssl
jerryscript::core
jerryscript::ext
jerryscript::port
) )
target_include_directories(${DUSK_BINARY_TARGET_NAME} PRIVATE target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
${SDL2_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS}
) )
target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_SDL2 DUSK_SDL2
DUSK_OPENGL DUSK_OPENGL
DUSK_PSP DUSK_PSP
@@ -61,7 +44,7 @@ target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC
DUSK_OPENGL_LEGACY DUSK_OPENGL_LEGACY
DUSK_DISPLAY_WIDTH=480 DUSK_DISPLAY_WIDTH=480
DUSK_DISPLAY_HEIGHT=272 DUSK_DISPLAY_HEIGHT=272
DUSK_THREAD_PTHREAD THREAD_PTHREAD=1
) )
# Postbuild, create .pbp file for PSP. # Postbuild, create .pbp file for PSP.
+22
View File
@@ -20,9 +20,31 @@ set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE) set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
find_package(cglm REQUIRED) find_package(cglm REQUIRED)
# Compile lua
include(FetchContent)
FetchContent_Declare(
liblua
URL https://www.lua.org/ftp/lua-5.5.0.tar.gz
)
FetchContent_MakeAvailable(liblua)
set(LUA_SRC_DIR "${liblua_SOURCE_DIR}/src")
set(LUA_C_FILES
lapi.c lauxlib.c lbaselib.c lcode.c lcorolib.c lctype.c ldblib.c ldebug.c
ldo.c ldump.c lfunc.c lgc.c linit.c liolib.c llex.c lmathlib.c lmem.c
loadlib.c lobject.c lopcodes.c loslib.c lparser.c lstate.c lstring.c
lstrlib.c ltable.c ltablib.c ltm.c lundump.c lutf8lib.c lvm.c lzio.c
)
list(TRANSFORM LUA_C_FILES PREPEND "${LUA_SRC_DIR}/")
add_library(liblua STATIC ${LUA_C_FILES})
target_include_directories(liblua PUBLIC "${LUA_SRC_DIR}")
target_compile_definitions(liblua PRIVATE LUA_USE_C89)
add_library(lua::lua ALIAS liblua)
set(Lua_FOUND TRUE CACHE BOOL "Lua found" FORCE)
# Link libraries # Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
${SDL2_LIBRARIES} ${SDL2_LIBRARIES}
liblua
cglm cglm
SDL2 SDL2
SDL2main SDL2main
+13 -9
View File
@@ -32,13 +32,18 @@ if(NOT yyjson_FOUND)
endif() endif()
endif() endif()
if(NOT jerryscript_FOUND) if(NOT Lua_FOUND)
find_package(jerryscript REQUIRED) find_package(Lua REQUIRED)
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE if(Lua_FOUND AND NOT TARGET Lua::Lua)
jerryscript::core add_library(Lua::Lua INTERFACE IMPORTED)
jerryscript::ext set_target_properties(
jerryscript::port Lua::Lua
) PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
)
endif()
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC Lua::Lua)
endif() endif()
# Includes # Includes
@@ -56,9 +61,8 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
# Subdirs # Subdirs
add_subdirectory(assert) add_subdirectory(assert)
add_subdirectory(asset) add_subdirectory(asset)
add_subdirectory(console)
add_subdirectory(display)
add_subdirectory(log) add_subdirectory(log)
add_subdirectory(display)
add_subdirectory(engine) add_subdirectory(engine)
add_subdirectory(entity) add_subdirectory(entity)
add_subdirectory(error) add_subdirectory(error)
+2 -2
View File
@@ -25,7 +25,7 @@ errorret_t assetInit(void) {
} }
bool_t assetFileExists(const char_t *filename) { bool_t assetFileExists(const char_t *filename) {
assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long."); assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0); zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0);
if(idx < 0) return false; if(idx < 0) return false;
@@ -38,7 +38,7 @@ errorret_t assetLoad(
void *params, void *params,
void *output void *output
) { ) {
assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long."); assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
assertNotNull(output, "Output pointer cannot be NULL."); assertNotNull(output, "Output pointer cannot be NULL.");
assertNotNull(loader, "Asset file loader cannot be NULL."); assertNotNull(loader, "Asset file loader cannot be NULL.");
-2
View File
@@ -9,8 +9,6 @@
#include "error/error.h" #include "error/error.h"
#include <zip.h> #include <zip.h>
#define ASSET_FILE_PATH_MAX FILENAME_MAX
typedef struct assetfile_s assetfile_t; typedef struct assetfile_s assetfile_t;
typedef errorret_t (*assetfileloader_t)(assetfile_t *file); typedef errorret_t (*assetfileloader_t)(assetfile_t *file);
@@ -1,92 +1,82 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#include "assetscriptloader.h" #include "assetscriptloader.h"
#include "assert/assert.h" #include "assert/assert.h"
#include <stdlib.h>
#include <zip.h>
errorret_t assetScriptLoader(assetfile_t *file) { errorret_t assetScriptLoader(assetfile_t *file) {
assertNotNull(file, "Asset file cannot be NULL"); assertNotNull(file, "Asset file cannot be NULL");
assertNull(file->zipFile, "Asset file zip handle must be NULL before open"); assertNull(file->zipFile, "Asset file zip handle must be NULL");
assertNotNull(file->output, "Asset file output cannot be NULL"); assertNotNull(file->output, "Asset file output cannot be NULL");
assetscript_t *script = (assetscript_t *)file->output; assetscript_t *script = (assetscript_t *)file->output;
// Open the asset for buffering
errorChain(assetFileOpen(file)); errorChain(assetFileOpen(file));
// Accumulate full source into a dynamically grown buffer. // Request loading
size_t srcLen = 0; if(!lua_load(
size_t capacity = ASSET_SCRIPT_CHUNK_SIZE; script->ctx->luaState,
char_t *src = (char_t *)malloc(capacity + 1); assetScriptReader,
if(!src) { file,
assetFileClose(file); file->filename,
errorThrow("Out of memory reading script: %s", file->filename); NULL
) == LUA_OK) {
const char_t *strErr = lua_tostring(script->ctx->luaState, -1);
lua_pop(script->ctx->luaState, 1);
errorThrow("Failed to load Lua script: %s", strErr);
} }
while(1) { // Now loaded, exec
if(srcLen + ASSET_SCRIPT_CHUNK_SIZE > capacity) { if(lua_pcall(script->ctx->luaState, 0, LUA_MULTRET, 0) != LUA_OK) {
capacity = srcLen + ASSET_SCRIPT_CHUNK_SIZE; const char_t *strErr = lua_tostring(script->ctx->luaState, -1);
char_t *tmp = (char_t *)realloc(src, capacity + 1); lua_pop(script->ctx->luaState, 1);
if(!tmp) { errorThrow("Failed to execute Lua script: %s", strErr);
free(src);
assetFileClose(file);
errorThrow("Out of memory reading script: %s", file->filename);
}
src = tmp;
}
zip_int64_t n = zip_fread(
file->zipFile, src + srcLen, ASSET_SCRIPT_CHUNK_SIZE
);
if(n <= 0) break;
srcLen += (size_t)n;
}
src[srcLen] = '\0';
errorret_t closeRet = assetFileClose(file);
jerry_value_t result = jerry_eval(
(const jerry_char_t *)src,
srcLen,
JERRY_PARSE_NO_OPTS
);
free(src);
if(jerry_value_is_exception(result)) {
jerry_value_t errVal = jerry_exception_value(result, false);
jerry_value_t errStr = jerry_value_to_string(errVal);
char_t buf[256];
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
);
buf[len] = '\0';
jerry_value_free(errStr);
jerry_value_free(errVal);
jerry_value_free(result);
errorThrow("Script error in '%s': %s", file->filename, buf);
} }
if(script->resultOut != NULL) { // Close the file
*(script->resultOut) = result; return assetFileClose(file);
} else {
jerry_value_free(result);
}
return closeRet;
} }
errorret_t assetScriptLoad( errorret_t assetScriptLoad(const char_t *path, scriptcontext_t *ctx) {
const char_t *path,
jerry_value_t *resultOut
) {
assertNotNull(path, "Script path cannot be NULL"); assertNotNull(path, "Script path cannot be NULL");
assertNotNull(ctx, "Script context cannot be NULL");
assetscript_t script;
script.ctx = ctx;
assetscript_t scriptData; return assetLoad(
scriptData.resultOut = resultOut; path,
assetScriptLoader,
return assetLoad(path, assetScriptLoader, NULL, &scriptData); NULL,
&script
);
} }
const char_t * assetScriptReader(lua_State* L, void* data, size_t* size) {
assetfile_t *file = (assetfile_t*)data;
assertNotNull(file, "Script asset file cannot be NULL");
assertNotNull(file->zipFile, "Script asset zip handle cannot be NULL");
assertNotNull(file->output, "Script asset output cannot be NULL");
assetscript_t *script = (assetscript_t *)file->output;
assertNotNull(script, "Script asset output cannot be NULL");
zip_int64_t read = zip_fread(
file->zipFile,
script->buffer,
sizeof(script->buffer)
);
if(read < 0) {
*size = 0;
return NULL;
}
*size = (size_t)read;
return script->buffer;
}
@@ -1,23 +1,28 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "asset/asset.h" #include "asset/asset.h"
#include "script/scriptcontext.h"
#define ASSET_SCRIPT_CHUNK_SIZE 1024 #define ASSET_SCRIPT_BUFFER_SIZE 1024
typedef struct { typedef struct {
jerry_value_t *resultOut; void *nothing;
} assetscriptloaderparams_t;
typedef struct {
scriptcontext_t *ctx;
char_t buffer[ASSET_SCRIPT_BUFFER_SIZE];
} assetscript_t; } assetscript_t;
/** /**
* Handler for script assets. Reads the full source, evaluates it with * Handler for script assets.
* JerryScript, and optionally stores the result. *
*
* @param file Asset file to load the script from. * @param file Asset file to load the script from.
* @return Any error that occurs during loading. * @return Any error that occurs during loading.
*/ */
@@ -25,14 +30,19 @@ errorret_t assetScriptLoader(assetfile_t *file);
/** /**
* Loads a script from the specified path. * Loads a script from the specified path.
* *
* @param path Path to the script asset. * @param path Path to the script asset.
* @param resultOut Optional out-parameter for the script return value. * @param ctx Script context to load the script into.
* Caller must call jerry_value_free() if non-NULL.
* Pass NULL to discard the return value.
* @return Any error that occurs during loading. * @return Any error that occurs during loading.
*/ */
errorret_t assetScriptLoad( errorret_t assetScriptLoad(const char_t *path, scriptcontext_t *ctx);
const char_t *path,
jerry_value_t *resultOut /**
); * Reader function for Lua to read script data from the asset.
*
* @param L Lua state.
* @param data Pointer to the scriptcontext_t structure.
* @param size Pointer to store the size of the read data.
* @return Pointer to the read data buffer.
*/
const char_t * assetScriptReader(lua_State* L, void* data, size_t* size);
-193
View File
@@ -1,193 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "console.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "input/input.h"
#include "log/log.h"
#include "engine/engine.h"
#include "script/scriptmanager.h"
#include "display/shader/shaderunlit.h"
#include "display/text/text.h"
#include "display/spritebatch/spritebatch.h"
console_t CONSOLE;
void consoleInit(void) {
memoryZero(&CONSOLE, sizeof(console_t));
#ifdef DUSK_CONSOLE_POSIX
threadInit(&CONSOLE.thread, consoleInputThread);
threadMutexInit(&CONSOLE.execMutex);
threadMutexInit(&CONSOLE.printMutex);
threadStartRequest(&CONSOLE.thread);
#endif
}
void consolePrint(const char_t *message, ...) {
char_t buffer[CONSOLE_LINE_MAX];
va_list args;
va_start(args, message);
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
va_end(args);
#ifdef DUSK_CONSOLE_POSIX
threadMutexLock(&CONSOLE.printMutex);
#endif
memoryMove(
CONSOLE.line[0],
CONSOLE.line[1],
(CONSOLE_HISTORY_MAX - 1) * CONSOLE_LINE_MAX
);
memoryCopy(CONSOLE.line[CONSOLE_HISTORY_MAX - 1], buffer, len + 1);
#ifdef DUSK_CONSOLE_POSIX
threadMutexUnlock(&CONSOLE.printMutex);
#endif
logDebug("%s\n", buffer);
}
void consoleExec(const char_t *line) {
assertNotNull(line, "line must not be NULL");
#ifdef DUSK_CONSOLE_POSIX
threadMutexLock(&CONSOLE.execMutex);
#endif
assertTrue(
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
"Console exec buffer is full"
);
stringCopy(
CONSOLE.execBuffer[CONSOLE.execBufferCount],
line,
CONSOLE_LINE_MAX
);
CONSOLE.execBufferCount++;
#ifdef DUSK_CONSOLE_POSIX
threadMutexUnlock(&CONSOLE.execMutex);
#endif
}
void consoleUpdate(void) {
#ifdef DUSK_TIME_DYNAMIC
if(TIME.dynamicUpdate) return;
#endif
if(inputPressed(INPUT_ACTION_CONSOLE)) {
CONSOLE.visible = !CONSOLE.visible;
}
if(CONSOLE.execBufferCount == 0) return;
#ifdef DUSK_CONSOLE_POSIX
threadMutexLock(&CONSOLE.execMutex);
#endif
char_t execBuffer[CONSOLE_EXEC_BUFFER_MAX][CONSOLE_LINE_MAX];
uint32_t execBufferCount = CONSOLE.execBufferCount;
memoryCopy(execBuffer, CONSOLE.execBuffer, sizeof(execBuffer));
CONSOLE.execBufferCount = 0;
#ifdef DUSK_CONSOLE_POSIX
threadMutexUnlock(&CONSOLE.execMutex);
#endif
for(uint32_t i = 0; i < execBufferCount; i++) {
jerry_value_t result = 0;
errorret_t err = scriptManagerExec(execBuffer[i], &result);
if(err.code != ERROR_OK) {
consolePrint("Error: %s", err.state->message);
errorCatch(err);
} else if(!jerry_value_is_undefined(result)) {
jerry_value_t strVal = jerry_value_to_string(result);
char_t buf[CONSOLE_LINE_MAX];
jerry_size_t len = jerry_string_to_buffer(
strVal, JERRY_ENCODING_UTF8, (jerry_char_t*)buf, sizeof(buf) - 1
);
buf[len] = '\0';
jerry_value_free(strVal);
consolePrint("%s", buf);
}
if(result != 0) jerry_value_free(result);
}
}
errorret_t consoleDraw(void) {
if(!CONSOLE.visible) errorOk();
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
errorChain(textDraw(
0, FONT_TILESET_DEFAULT.tileHeight * i,
CONSOLE.line[i],
COLOR_WHITE,
&FONT_TILESET_DEFAULT,
&FONT_TEXTURE_DEFAULT
));
}
errorChain(spriteBatchFlush());
errorOk();
}
void consoleDispose(void) {
#ifdef DUSK_CONSOLE_POSIX
threadStop(&CONSOLE.thread);
threadMutexDispose(&CONSOLE.execMutex);
threadMutexDispose(&CONSOLE.printMutex);
#endif
}
#ifdef DUSK_CONSOLE_POSIX
void consoleInputThread(thread_t *thread) {
assertNotNull(thread, "Thread cannot be NULL.");
char_t line[CONSOLE_LINE_MAX];
struct pollfd pfd = {
.fd = STDIN_FILENO,
.events = POLLIN
};
while(!threadShouldStop(thread) && ENGINE.running) {
int32_t rc = poll(&pfd, 1, CONSOLE_POSIX_POLL_RATE);
if(rc == 0) continue;
if(rc < 0) {
if(errno == EINTR) continue;
assertUnreachable("poll() failed with unexpected error.");
}
if(pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) break;
if(!(pfd.revents & POLLIN)) {
pfd.revents = 0;
continue;
}
if(!fgets(line, CONSOLE_LINE_MAX, stdin)) {
if(feof(stdin)) break;
clearerr(stdin);
continue;
}
size_t len = strlen(line);
while(len && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
line[--len] = '\0';
}
if(len > 0) consoleExec(line);
pfd.revents = 0;
}
}
#endif
-79
View File
@@ -1,79 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "consoledefs.h"
#include "error/error.h"
#include "dusk.h"
#ifdef DUSK_CONSOLE_POSIX
#include "thread/thread.h"
#include <poll.h>
#include <unistd.h>
#define CONSOLE_POSIX_POLL_RATE 75
#endif
typedef struct {
char_t line[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX];
bool_t visible;
char_t execBuffer[CONSOLE_EXEC_BUFFER_MAX][CONSOLE_LINE_MAX];
uint32_t execBufferCount;
#ifdef DUSK_CONSOLE_POSIX
thread_t thread;
threadmutex_t execMutex;
threadmutex_t printMutex;
#endif
} console_t;
extern console_t CONSOLE;
/**
* Initializes the console.
*/
void consoleInit(void);
/**
* Prints a message to the console history.
*
* @param message The message to print (printf-style).
*/
void consolePrint(const char_t *message, ...);
/**
* Queues a JS string for execution on the main thread. Thread-safe.
*
* @param line The JS source line to execute.
*/
void consoleExec(const char_t *line);
/**
* Processes pending queued script lines. Call once per frame from main thread.
*/
void consoleUpdate(void);
/**
* Renders the console history to the screen (UI space).
*
* @return The error return value.
*/
errorret_t consoleDraw(void);
/**
* Disposes of the console.
*/
void consoleDispose(void);
#ifdef DUSK_CONSOLE_POSIX
/**
* Input thread handler for POSIX stdin.
*
* @param thread The thread that is running.
*/
void consoleInputThread(thread_t *thread);
#endif
-12
View File
@@ -1,12 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#define CONSOLE_LINE_MAX 512
#define CONSOLE_HISTORY_MAX 16
#define CONSOLE_EXEC_BUFFER_MAX 32
+2
View File
@@ -25,6 +25,8 @@
#include "display/shader/shaderunlit.h" #include "display/shader/shaderunlit.h"
#include "time/time.h" #include "time/time.h"
#include "script/module/display/moduleshader.h"
display_t DISPLAY = { 0 }; display_t DISPLAY = { 0 };
errorret_t displayInit(void) { errorret_t displayInit(void) {
+5 -5
View File
@@ -13,19 +13,19 @@
#include "asset/loader/display/assettilesetloader.h" #include "asset/loader/display/assettilesetloader.h"
#include "display/shader/shaderunlit.h" #include "display/shader/shaderunlit.h"
texture_t FONT_TEXTURE_DEFAULT; texture_t DEFAULT_FONT_TEXTURE;
tileset_t FONT_TILESET_DEFAULT; tileset_t DEFAULT_FONT_TILESET;
errorret_t textInit(void) { errorret_t textInit(void) {
errorChain(assetTextureLoad( errorChain(assetTextureLoad(
"ui/minogram.png", &FONT_TEXTURE_DEFAULT, TEXTURE_FORMAT_RGBA "ui/minogram.png", &DEFAULT_FONT_TEXTURE, TEXTURE_FORMAT_RGBA
)); ));
errorChain(assetTilesetLoad("ui/minogram.dtf", &FONT_TILESET_DEFAULT)); errorChain(assetTilesetLoad("ui/minogram.dtf", &DEFAULT_FONT_TILESET));
errorOk(); errorOk();
} }
errorret_t textDispose(void) { errorret_t textDispose(void) {
errorChain(textureDispose(&FONT_TEXTURE_DEFAULT)); errorChain(textureDispose(&DEFAULT_FONT_TEXTURE));
errorOk(); errorOk();
} }
+2 -2
View File
@@ -12,8 +12,8 @@
#define TEXT_CHAR_START '!' #define TEXT_CHAR_START '!'
extern texture_t FONT_TEXTURE_DEFAULT; extern texture_t DEFAULT_FONT_TEXTURE;
extern tileset_t FONT_TILESET_DEFAULT; extern tileset_t DEFAULT_FONT_TILESET;
/** /**
* Initializes the text system. * Initializes the text system.
-1
View File
@@ -21,7 +21,6 @@
#include <cglm/cglm.h> #include <cglm/cglm.h>
#include <cglm/types.h> #include <cglm/types.h>
#include <cglm/vec2.h> #include <cglm/vec2.h>
#include <jerryscript.h>
#include "duskplatform.h" #include "duskplatform.h"
+168 -23
View File
@@ -21,20 +21,103 @@
#include "game/game.h" #include "game/game.h"
#include "physics/physicsmanager.h" #include "physics/physicsmanager.h"
#include "network/network.h" #include "network/network.h"
#include "network/networkinfo.h"
#include "network/networksocketclient.h"
#include "system/system.h" #include "system/system.h"
#include "console/console.h"
double jerry_port_current_time(void) { #include "display/mesh/cube.h"
dusktimeepoch_t epoch = timeGetEpoch(); #include "display/mesh/plane.h"
return epoch.time * 1000.0;
}
int32_t jerry_port_local_tza(double unix_ms) {
(void) unix_ms;
return 0;
}
engine_t ENGINE; engine_t ENGINE;
entityid_t phBoxEnt;
componentid_t phBoxPhys;
networksocketclient_t sockClient;
float_t onlineSwapTime = FLT_MAX;
void goOnline();
void goOffline();
void onSocketConnected(void *user) {
sceneLog("Socket connected.\n");
}
void onSocketError(errorret_t error, void *user) {
sceneLog("Socket error: %s\n", error.state->message);
errorCatch(errorPrint(error));
}
void onSocketDisconnected(void *user) {
sceneLog("Socket disconnected.\n");
}
void onNetworkConnected(void *user) {
onlineSwapTime = TIME.time + 1.5f;
networkinfo_t info = networkGetInfo();
if(info.type == NETWORK_TYPE_IPV4) {
sceneLog(
"Connected to network with IPv4 address: " NETWORK_INFO_FORMAT_IPV4 "\n",
info.ipv4.ip[0], info.ipv4.ip[1], info.ipv4.ip[2], info.ipv4.ip[3]
);
#ifdef DUSK_NETWORK_IPV6
} else if(info.type == NETWORK_TYPE_IPV6) {
sceneLog(
"Connected to network with IPv6 address: " NETWORK_INFO_FORMAT_IPV6 "\n",
info.ipv6.ip[0], info.ipv6.ip[1], info.ipv6.ip[2], info.ipv6.ip[3],
info.ipv6.ip[4], info.ipv6.ip[5], info.ipv6.ip[6], info.ipv6.ip[7],
info.ipv6.ip[8], info.ipv6.ip[9], info.ipv6.ip[10], info.ipv6.ip[11],
info.ipv6.ip[12], info.ipv6.ip[13], info.ipv6.ip[14], info.ipv6.ip[15]
);
#endif
}
sceneLog("Network connected, opening socket: %.2f1.\n", onlineSwapTime);
networkSocketClientInit(
&sockClient,
"google.com",
443,
NULL,
onSocketConnected,
onSocketError,
onSocketDisconnected
);
onlineSwapTime = FLT_MAX;
// sceneLog("Network connected, I will disconnect at: %.2f1.\n", onlineSwapTime);
}
void onNetworkFailed(errorret_t error, void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Failed to connect to network, will try again at %.2f1.\n", onlineSwapTime);
}
void onNetworkDisconnected(errorret_t error, void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Network disconnected, will go online at %.2f1.\n", onlineSwapTime);
errorCatch(errorPrint(error));
}
void onNetworkDisconnectFinished(void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Finished disconnecting from network, will go online at %.2f1.\n", onlineSwapTime);
}
void goOnline() {
sceneLog("Going online...\n");
networkRequestConnection(
onNetworkConnected,
onNetworkFailed,
onNetworkDisconnected,
NULL
);
}
void goOffline() {
sceneLog("Going offline...\n");
networkRequestDisconnection(onNetworkDisconnectFinished, NULL);
}
errorret_t engineInit(const int32_t argc, const char_t **argv) { errorret_t engineInit(const int32_t argc, const char_t **argv) {
memoryZero(&ENGINE, sizeof(engine_t)); memoryZero(&ENGINE, sizeof(engine_t));
@@ -45,7 +128,6 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
// Init systems. Order is important. // Init systems. Order is important.
errorChain(systemInit()); errorChain(systemInit());
timeInit(); timeInit();
consoleInit();
errorChain(inputInit()); errorChain(inputInit());
errorChain(assetInit()); errorChain(assetInit());
errorChain(localeManagerInit()); errorChain(localeManagerInit());
@@ -55,32 +137,97 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(sceneInit()); errorChain(sceneInit());
entityManagerInit(); entityManagerInit();
physicsManagerInit(); physicsManagerInit();
errorChain(networkInit()); // errorChain(networkInit());
errorChain(gameInit()); errorChain(gameInit());
onlineSwapTime = TIME.time + 1.0f;
sceneLog("Init done, going to queue online at %.2f1.\n", onlineSwapTime);
// Camera
entityid_t cam = entityManagerAdd();
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
float_t distance = 6.0f;
entityPositionLookAt(
cam, camPos,
(vec3){ 0.0f, 1.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f },
(vec3){ distance, distance, distance }
);
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
entityCameraSetZFar(cam, camCam, 100.0f);
// Floor
entityid_t floorEnt = entityManagerAdd();
componentid_t floorPos = entityAddComponent(floorEnt, COMPONENT_TYPE_POSITION);
componentid_t floorMesh = entityAddComponent(floorEnt, COMPONENT_TYPE_MESH);
componentid_t floorMat = entityAddComponent(floorEnt, COMPONENT_TYPE_MATERIAL);
componentid_t floorPhys = entityAddComponent(floorEnt, COMPONENT_TYPE_PHYSICS);
entityPositionSetPosition(floorEnt, floorPos, (vec3){ -5.0f, 0.0f, -5.0f });
entityPositionSetScale(floorEnt, floorPos, (vec3){ 10.0f, 1.0f, 10.0f });
entityMeshSetMesh(floorEnt, floorMesh, &PLANE_MESH_SIMPLE);
entityMaterialGetShaderMaterial(floorEnt, floorMat)->unlit.color = COLOR_GREEN;
entityphysics_t *floorPhysData = entityPhysicsGet(floorEnt, floorPhys);
floorPhysData->type = PHYSICS_BODY_STATIC;
floorPhysData->shape.type = PHYSICS_SHAPE_PLANE;
floorPhysData->shape.data.plane.normal[0] = 0.0f;
floorPhysData->shape.data.plane.normal[1] = 1.0f;
floorPhysData->shape.data.plane.normal[2] = 0.0f;
floorPhysData->shape.data.plane.distance = 0.0f;
// Box
phBoxEnt = entityManagerAdd();
componentid_t boxPos = entityAddComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
componentid_t boxMesh = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MESH);
componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL);
phBoxPhys = entityAddComponent(phBoxEnt, COMPONENT_TYPE_PHYSICS);
entityMeshSetMesh(phBoxEnt, boxMesh, &CUBE_MESH_SIMPLE);
entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED;
entityPositionSetPosition(phBoxEnt, boxPos, (vec3){ 0.0f, 4.0f, 0.0f });
/* Run the init script. */ /* Run the init script. */
consolePrint("Engine initialized"); scriptcontext_t ctx;
errorChain(scriptManagerExecFile("init.js", NULL)); errorChain(scriptContextInit(&ctx));
errorChain(scriptContextExecFile(&ctx, "init.lua"));
scriptContextDispose(&ctx);
errorOk(); errorOk();
} }
errorret_t engineUpdate(void) { errorret_t engineUpdate(void) {
// Order here is important. // errorChain(networkUpdate());
errorChain(networkUpdate());
timeUpdate(); timeUpdate();
inputUpdate(); inputUpdate();
consoleUpdate();
uiUpdate(); uiUpdate();
errorChain(sceneUpdate());
/* Reset the box to its start position on demand. */
if(inputIsDown(INPUT_ACTION_ACCEPT)) {
componentid_t posComp = entityGetComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
entityPositionSetPosition(phBoxEnt, posComp, (vec3){ 0.0f, 4.0f, 0.0f });
entityPhysicsSetVelocity(phBoxEnt, phBoxPhys, (vec3){ 0.0f, 0.0f, 0.0f });
}
/* Step physics: positions are updated directly on POSITION components. */
physicsManagerUpdate(); physicsManagerUpdate();
errorChain(gameUpdate()); errorChain(gameUpdate());
errorChain(displayUpdate()); errorChain(displayUpdate());
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false; if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
// Scene update occurs last because only after rendering would we want to do if(TIME.time >= onlineSwapTime) {
// scene switching, refer to sceneSet() for information. onlineSwapTime = FLT_MAX;
errorChain(sceneUpdate()); if(NETWORK.state == NETWORK_STATE_CONNECTED) {
goOffline();
} else {
goOnline();
}
}
errorOk(); errorOk();
} }
@@ -90,15 +237,13 @@ void engineExit(void) {
} }
errorret_t engineDispose(void) { errorret_t engineDispose(void) {
// errorChain(networkDispose());
sceneDispose(); sceneDispose();
errorChain(gameDispose()); errorChain(gameDispose());
errorChain(networkDispose());
entityManagerDispose(); entityManagerDispose();
localeManagerDispose(); localeManagerDispose();
uiDispose(); uiDispose();
consoleDispose();
errorChain(displayDispose()); errorChain(displayDispose());
errorChain(assetDispose()); errorChain(assetDispose());
errorOk(); errorOk();
} }
+2 -8
View File
@@ -12,14 +12,8 @@
componentdefinition_t COMPONENT_DEFINITIONS[] = { componentdefinition_t COMPONENT_DEFINITIONS[] = {
[COMPONENT_TYPE_NULL] = { 0 }, [COMPONENT_TYPE_NULL] = { 0 },
#define X(enm, type, field, iMethod, dMethod) \ #define X(enumName, type, field, iMethod, dMethod) \
[COMPONENT_TYPE_##enm] = { \ [COMPONENT_TYPE_##enumName] = { .init = iMethod, .dispose = dMethod },
.enumName = #enm, \
.name = #field, \
.init = iMethod, \
.dispose = dMethod \
},
#include "componentlist.h" #include "componentlist.h"
#undef X #undef X
-2
View File
@@ -20,8 +20,6 @@ typedef union {
} componentdata_t; } componentdata_t;
typedef struct { typedef struct {
const char_t *enumName;
const char_t *name;
void (*init)(const entityid_t, const componentid_t); void (*init)(const entityid_t, const componentid_t);
void (*dispose)(const entityid_t, const componentid_t); void (*dispose)(const entityid_t, const componentid_t);
} componentdefinition_t; } componentdefinition_t;
+1 -2
View File
@@ -4,5 +4,4 @@
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_subdirectory(display) add_subdirectory(display)
add_subdirectory(physics) add_subdirectory(physics)
add_subdirectory(script)
@@ -6,8 +6,6 @@
*/ */
#include "entity/entitymanager.h" #include "entity/entitymanager.h"
#include "entity/entity.h"
#include "entity/component/display/entityposition.h"
#include "display/framebuffer/framebuffer.h" #include "display/framebuffer/framebuffer.h"
#include "display/screen/screen.h" #include "display/screen/screen.h"
@@ -21,6 +19,42 @@ void entityCameraInit(const entityid_t ent, const componentid_t comp) {
cam->perspective.fov = glm_rad(45.0f); cam->perspective.fov = glm_rad(45.0f);
} }
float_t entityCameraGetZNear(const entityid_t ent, const componentid_t comp) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
return cam->nearClip;
}
void entityCameraSetZNear(
const entityid_t ent,
const componentid_t comp,
const float_t zNear
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->nearClip = zNear;
}
float_t entityCameraGetZFar(const entityid_t ent, const componentid_t comp) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
return cam->farClip;
}
void entityCameraSetZFar(
const entityid_t ent,
const componentid_t comp,
const float_t zFar
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->farClip = zFar;
}
void entityCameraGetProjection( void entityCameraGetProjection(
const entityid_t ent, const entityid_t ent,
const componentid_t comp, const componentid_t comp,
@@ -58,38 +92,4 @@ void entityCameraGetProjection(
out 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_COUNT_MAX;
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->transform[0][2];
float_t fz = -pos->transform[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 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->transform[0][0];
float_t rz = pos->transform[2][0];
float_t len = sqrtf(rx * rx + rz * rz);
if(len > 1e-6f) { rx /= len; rz /= len; }
out[0] = rx;
out[1] = rz;
} }
@@ -55,24 +55,45 @@ void entityCameraGetProjection(
); );
/** /**
* Returns the entity ID of the first active camera, or ENTITY_COUNT_MAX if none. * Gets the near clip distance of a camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @return The near clip distance.
*/ */
entityid_t entityCameraGetCurrent(void); float_t entityCameraGetZNear(const entityid_t ent, const componentid_t comp);
/** /**
* Gets the camera's horizontal forward direction (XZ plane) from its position * Sets the near clip distance of a camera.
* component. Automatically finds the position component on the entity.
* *
* @param entityId The camera entity ID. * @param ent The entity ID.
* @param out Output vec2: {forwardX, forwardZ} normalized. * @param comp The component ID.
* @param zNear The near clip distance.
*/ */
void entityCameraGetForward(const entityid_t entityId, vec2 out); void entityCameraSetZNear(
const entityid_t ent,
const componentid_t comp,
const float_t zNear
);
/** /**
* Gets the camera's horizontal right direction (XZ plane) from its position * Gets the far clip distance of a camera.
* component. Automatically finds the position component on the entity.
* *
* @param entityId The camera entity ID. * @param ent The entity ID.
* @param out Output vec2: {rightX, rightZ} normalized. * @param comp The component ID.
* @return The far clip distance.
*/ */
void entityCameraGetRight(const entityid_t entityId, vec2 out); float_t entityCameraGetZFar(const entityid_t ent, const componentid_t comp);
/**
* Sets the far clip distance of a camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @param zFar The far clip distance.
*/
void entityCameraSetZFar(
const entityid_t ent,
const componentid_t comp,
const float_t zFar
);
@@ -48,15 +48,4 @@ void entityMaterialSetShader(
entityId, componentId, COMPONENT_TYPE_MATERIAL entityId, componentId, COMPONENT_TYPE_MATERIAL
); );
mat->shader = shader; mat->shader = shader;
}
void entityMaterialSetColor(
const entityid_t entityId,
const componentid_t componentId,
const color_t color
) {
entitymaterial_t *mat = componentGetData(
entityId, componentId, COMPONENT_TYPE_MATERIAL
);
mat->material.unlit.color = color;
} }
@@ -60,17 +60,4 @@ void entityMaterialSetShader(
const entityid_t entityId, const entityid_t entityId,
const componentid_t componentId, const componentid_t componentId,
shader_t *shader shader_t *shader
);
/**
* Sets the unlit color for the given entity and component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param color The color to set.
*/
void entityMaterialSetColor(
const entityid_t entityId,
const componentid_t componentId,
const color_t color
); );
+1 -105
View File
@@ -5,13 +5,7 @@
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#include "entitymesh.h"
#include "entity/entitymanager.h" #include "entity/entitymanager.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "display/mesh/cube.h"
#include "display/mesh/plane.h"
#include "display/mesh/capsule.h"
void entityMeshInit( void entityMeshInit(
const entityid_t entityId, const entityid_t entityId,
@@ -20,9 +14,7 @@ void entityMeshInit(
entitymesh_t *comp = componentGetData( entitymesh_t *comp = componentGetData(
entityId, componentId, COMPONENT_TYPE_MESH entityId, componentId, COMPONENT_TYPE_MESH
); );
comp->mesh = &CUBE_MESH_SIMPLE; comp->mesh = NULL;
comp->ownedVertices = NULL;
comp->ownsData = false;
} }
mesh_t * entityMeshGetMesh( mesh_t * entityMeshGetMesh(
@@ -44,100 +36,4 @@ void entityMeshSetMesh(
entityId, componentId, COMPONENT_TYPE_MESH entityId, componentId, COMPONENT_TYPE_MESH
); );
comp->mesh = mesh; comp->mesh = mesh;
}
void entityMeshDispose(
const entityid_t entityId,
const componentid_t componentId
) {
entitymesh_t *comp = componentGetData(
entityId, componentId, COMPONENT_TYPE_MESH
);
if(!comp->ownsData) return;
(void)meshDispose(&comp->ownedMesh);
memoryFree(comp->ownedVertices);
comp->ownedVertices = NULL;
comp->ownsData = false;
comp->mesh = NULL;
}
errorret_t entityMeshGeneratePlane(
const entityid_t entityId,
const componentid_t componentId,
const float_t width,
const float_t height
) {
entitymesh_t *comp = componentGetData(
entityId, componentId, COMPONENT_TYPE_MESH
);
entityMeshDispose(entityId, componentId);
comp->ownedVertices = memoryAllocate(PLANE_VERTEX_COUNT * sizeof(meshvertex_t));
assertNotNull(comp->ownedVertices, "Failed to allocate plane vertices");
vec3 min = { -width * 0.5f, 0.0f, -height * 0.5f };
vec3 max = { width * 0.5f, 0.0f, height * 0.5f };
vec2 uvMin = { 0.0f, 0.0f };
vec2 uvMax = { 1.0f, 1.0f };
planeBuffer(
comp->ownedVertices,
PLANE_AXIS_XZ,
min,
max
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
, uvMin,
uvMax
);
errorChain(meshInit(
&comp->ownedMesh,
PLANE_PRIMITIVE_TYPE,
PLANE_VERTEX_COUNT,
comp->ownedVertices
));
comp->mesh = &comp->ownedMesh;
comp->ownsData = true;
errorOk();
}
errorret_t entityMeshGenerateCapsule(
const entityid_t entityId,
const componentid_t componentId,
const float_t radius,
const float_t halfHeight
) {
entitymesh_t *comp = componentGetData(
entityId, componentId, COMPONENT_TYPE_MESH
);
entityMeshDispose(entityId, componentId);
comp->ownedVertices = memoryAllocate(CAPSULE_VERTEX_COUNT * sizeof(meshvertex_t));
assertNotNull(comp->ownedVertices, "Failed to allocate capsule vertices");
vec3 center = { 0.0f, 0.0f, 0.0f };
capsuleBuffer(
comp->ownedVertices,
center,
radius,
halfHeight,
CAPSULE_CAP_RINGS,
CAPSULE_SECTORS
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&comp->ownedMesh,
CAPSULE_PRIMITIVE_TYPE,
CAPSULE_VERTEX_COUNT,
comp->ownedVertices
));
comp->mesh = &comp->ownedMesh;
comp->ownsData = true;
errorOk();
} }
@@ -11,9 +11,6 @@
typedef struct { typedef struct {
mesh_t *mesh; mesh_t *mesh;
mesh_t ownedMesh;
meshvertex_t *ownedVertices;
bool_t ownsData;
} entitymesh_t; } entitymesh_t;
/** /**
@@ -50,47 +47,4 @@ void entityMeshSetMesh(
const entityid_t entityId, const entityid_t entityId,
const componentid_t componentId, const componentid_t componentId,
mesh_t *mesh mesh_t *mesh
);
/**
* Disposes the entity mesh component, freeing any owned mesh data.
*
* @param entityId The entity ID.
* @param componentId The component ID.
*/
void entityMeshDispose(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Generates an XZ-aligned plane mesh owned by the component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param width Width of the plane along the X axis.
* @param height Height of the plane along the Z axis.
* @return Error return value.
*/
errorret_t entityMeshGeneratePlane(
const entityid_t entityId,
const componentid_t componentId,
const float_t width,
const float_t height
);
/**
* Generates a Y-axis capsule mesh owned by the component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param radius Radius of the cylinder and hemisphere caps.
* @param halfHeight Half-height of the cylindrical section.
* @return Error return value.
*/
errorret_t entityMeshGenerateCapsule(
const entityid_t entityId,
const componentid_t componentId,
const float_t radius,
const float_t halfHeight
); );
@@ -100,25 +100,6 @@ bool_t entityPhysicsIsOnGround(
return phys->onGround; 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( void entityPhysicsDispose(
const entityid_t entityId, const entityid_t entityId,
const componentid_t componentId const componentid_t componentId
@@ -122,31 +122,6 @@ bool_t entityPhysicsIsOnGround(
const componentid_t componentId 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 * Releases the body slot back to PHYSICS_WORLD. Called automatically when
* the component is disposed via the component system. * the component is disposed via the component system.
@@ -1,7 +0,0 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
+1 -7
View File
@@ -11,14 +11,8 @@
#include "entity/component/display/entitymaterial.h" #include "entity/component/display/entitymaterial.h"
#include "entity/component/physics/entityphysics.h" #include "entity/component/physics/entityphysics.h"
// Name (Uppercase)
// Structure
// Field name (lowercase)
// Init function (optional)
// Dispose function (optional)
X(POSITION, entityposition_t, position, entityPositionInit, NULL) X(POSITION, entityposition_t, position, entityPositionInit, NULL)
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL) X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
X(MESH, entitymesh_t, mesh, entityMeshInit, entityMeshDispose) X(MESH, entitymesh_t, mesh, entityMeshInit, NULL)
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL) X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose) X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
+2 -2
View File
@@ -8,8 +8,8 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#define ENTITY_COUNT_MAX 20 #define ENTITY_COUNT_MAX 6
#define ENTITY_COMPONENT_COUNT_MAX 8 #define ENTITY_COMPONENT_COUNT_MAX 6
typedef uint8_t entityid_t; typedef uint8_t entityid_t;
typedef uint8_t componentid_t; typedef uint8_t componentid_t;
+3 -3
View File
@@ -8,7 +8,7 @@
#include "entitymanager.h" #include "entitymanager.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include "console/console.h" #include "scene/scene.h"
entitymanager_t ENTITY_MANAGER; entitymanager_t ENTITY_MANAGER;
@@ -19,8 +19,8 @@ void entityManagerInit(void) {
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
); );
consolePrint( sceneLog(
"Entity Manager size: %zu bytes (%.2f KB)", "Entity Manager size: %zu bytes (%.2f KB)\n",
sizeof(entitymanager_t), sizeof(entitymanager_t),
sizeof(entitymanager_t) / 1024.0f sizeof(entitymanager_t) / 1024.0f
); );
+89 -76
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -8,7 +8,6 @@
#include "event.h" #include "event.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include "console/console.h"
void eventInit( void eventInit(
event_t *event, event_t *event,
@@ -47,16 +46,19 @@ eventsub_t eventSubscribeUser(
"Script event listener context cannot be NULL" "Script event listener context cannot be NULL"
); );
assertTrue( assertTrue(
user.script.funcValue != 0, user.script.luaFunctionRef != LUA_NOREF,
"Script event listener function reference is invalid" "Script event listener function reference is invalid"
); );
} else { } else {
assertUnreachable("Unknown event listener type"); assertUnreachable("Unknown event listener type");
} }
// Gen a new ID
eventsub_t id = event->nextId++; eventsub_t id = event->nextId++;
// Did we wrap?
assertTrue(event->nextId != 0, "Event subscription ID overflow"); assertTrue(event->nextId != 0, "Event subscription ID overflow");
// Append listener
eventlistener_t *listener = &event->listenerArray[event->listenerCount++]; eventlistener_t *listener = &event->listenerArray[event->listenerCount++];
memoryZero(listener, sizeof(eventlistener_t)); memoryZero(listener, sizeof(eventlistener_t));
listener->user = user; listener->user = user;
@@ -71,7 +73,7 @@ eventsub_t eventSubscribe(
const eventcallback_t callback, const eventcallback_t callback,
const void *user const void *user
) { ) {
return eventSubscribeUser( eventSubscribeUser(
event, event,
EVENT_TYPE_C, EVENT_TYPE_C,
(eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } } (eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } }
@@ -80,34 +82,54 @@ eventsub_t eventSubscribe(
eventsub_t eventSubscribeScriptContext( eventsub_t eventSubscribeScriptContext(
event_t *event, event_t *event,
scriptmanager_t *context, scriptcontext_t *context,
jerry_value_t funcValue const int functionIndex
) { ) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
assertTrue(funcValue != 0, "Script function value is invalid"); assertTrue(
lua_isfunction(context->luaState, functionIndex),
"Expected function at given index"
);
// Create a reference to the function
lua_pushvalue(context->luaState, functionIndex);
int funcRef = luaL_ref(context->luaState, LUA_REGISTRYINDEX);
eventscript_t scriptUser = {
.context = context,
.luaFunctionRef = funcRef
};
// Note to the context that it is now a part of this event
bool_t alreadySubbed = false; bool_t alreadySubbed = false;
uint8_t i = 0; uint8_t i;
i = 0;
do { do {
if(context->subscribedEvents[i] == event) { if(context->subscribedEvents[i] != event) {
alreadySubbed = true; i++;
break; continue;
} }
i++;
} while(i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS); if(context->subscribedEvents[i] == NULL) break;
alreadySubbed = true;
break;
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
if(!alreadySubbed) { if(!alreadySubbed) {
i = 0; i = 0;
do { do {
if(context->subscribedEvents[i] == NULL) { if(context->subscribedEvents[i] != NULL) {
context->subscribedEvents[i] = event; i++;
break; continue;
} }
i++;
} while(i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS); context->subscribedEvents[i] = event;
break;
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
assertTrue( assertTrue(
i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS, i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS,
"Script context has reached maximum event subscriptions" "Script context has reached maximum event subscriptions"
); );
} }
@@ -115,12 +137,7 @@ eventsub_t eventSubscribeScriptContext(
return eventSubscribeUser( return eventSubscribeUser(
event, event,
EVENT_TYPE_SCRIPT, EVENT_TYPE_SCRIPT,
(eventuserdata_t){ (eventuserdata_t){ .script = scriptUser }
.script = {
.context = context,
.funcValue = funcValue
}
}
); );
} }
@@ -130,34 +147,38 @@ void eventUnsubscribe(event_t *event, const eventsub_t id) {
if(event->listenerCount == 0) return; if(event->listenerCount == 0) return;
// Find listener
uint16_t index = 0; uint16_t index = 0;
do { do {
if(event->listenerArray[index].id != id) { if(event->listenerArray[index].id == id) {
index++; // Found it, remove by swapping with last and reducing count
continue; event->listenerArray[index] = event->listenerArray[--event->listenerCount];
return;
} }
index++;
if(event->listenerArray[index].type == EVENT_TYPE_SCRIPT) {
jerry_value_t funcVal = event->listenerArray[index].user.script.funcValue;
if(funcVal != 0) {
jerry_value_free(funcVal);
}
}
event->listenerArray[index] = event->listenerArray[--event->listenerCount];
return;
} while(index < event->listenerCount); } while(index < event->listenerCount);
// Did we find it?
if(index == event->listenerCount) return;
// Shift remaining listeners down (if any)
if(index < event->listenerCount - 1) {
memoryMove(
&event->listenerArray[index],
&event->listenerArray[index + 1],
sizeof(eventlistener_t) * (event->listenerCount - index - 1)
);
}
event->listenerCount--;
} }
void eventUnsubscribeScriptContext( void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) {
event_t *event,
const scriptmanager_t *ctx
) {
assertNotNull(event, "Event cannot be NULL"); assertNotNull(event, "Event cannot be NULL");
assertNotNull(ctx, "Script context cannot be NULL"); assertNotNull(ctx, "Script context cannot be NULL");
if(event->listenerCount == 0) return; if(event->listenerCount == 0) return;
uint16_t i = 0; uint16_t i = 0;
do { do {
eventlistener_t *listener = &event->listenerArray[i]; eventlistener_t *listener = &event->listenerArray[i];
@@ -168,6 +189,9 @@ void eventUnsubscribeScriptContext(
i++; i++;
continue; continue;
} }
// This listener belongs to the context and will need to go away. This will
// in turn decrement the listener count so we don't increment i here.
eventUnsubscribe(event, listener->id); eventUnsubscribe(event, listener->id);
} while(i < event->listenerCount); } while(i < event->listenerCount);
} }
@@ -180,55 +204,44 @@ void eventInvoke(
assertNotNull(event, "Event cannot be NULL"); assertNotNull(event, "Event cannot be NULL");
if(event->listenerCount == 0) return; if(event->listenerCount == 0) return;
event->isInvoking = true; event->isInvoking = true;
eventdata_t data = { uint16_t i = 0;
eventdata_t data ={
.event = event, .event = event,
.eventParams = eventParams, .eventParams = eventParams,
}; };
uint16_t i = 0;
do { do {
eventlistener_t *listener = &event->listenerArray[i]; eventlistener_t *listener = &event->listenerArray[i];
if(listener->type == EVENT_TYPE_C) { if(listener->type == EVENT_TYPE_C) {
listener->user.c.callback(&data, listener->user.c); listener->user.c.callback(&data, listener->user.c);
} else if(listener->type == EVENT_TYPE_SCRIPT) { } else if(listener->type == EVENT_TYPE_SCRIPT) {
jerry_value_t funcVal = listener->user.script.funcValue; // Call Lua function
assertNotNull((void *)(uintptr_t)funcVal, "Script function value is NULL"); lua_State *L = listener->user.script.context->luaState;
assertNotNull(L, "Lua state in event listener cannot be NULL");
jerry_value_t callArgs[1]; // Push function
jerry_length_t argCount = 0; lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
if(eventParams != NULL) { if(eventParams != NULL && metatableName != NULL) {
callArgs[0] = jerry_object(); lua_getmetatable(L, -1);
jerry_object_set_native_ptr( luaL_getmetatable(L, metatableName);
callArgs[0], &JS_PTR_NATIVE_INFO, (void *)eventParams assertTrue(
lua_rawequal(L, -1, -2),
"Event parameter metatable does not match expected type"
); );
argCount = 1;
} }
jerry_value_t result = jerry_call( // Call function with 1 arg, 0 return values
funcVal, jerry_undefined(), callArgs, argCount if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
); const char_t *strErr = lua_tostring(L, -1);
lua_pop(L, 1);
if(argCount > 0) jerry_value_free(callArgs[0]); // Log error but continue
printf("Error invoking Lua event listener: %s\n", strErr);
if(jerry_value_is_exception(result)) {
jerry_value_t errStr = jerry_value_to_string(
jerry_exception_value(result, false)
);
char_t buf[256];
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
);
buf[len] = '\0';
jerry_value_free(errStr);
consolePrint("Error invoking script event listener:\n%s\n", buf);
} }
jerry_value_free(result);
} else { } else {
assertUnreachable("Unknown event listener type"); assertUnreachable("Unknown event listener type");
} }
@@ -236,4 +249,4 @@ void eventInvoke(
} while(i < event->listenerCount); } while(i < event->listenerCount);
event->isInvoking = false; event->isInvoking = false;
} }
+3 -6
View File
@@ -83,8 +83,8 @@ eventsub_t eventSubscribe(
*/ */
eventsub_t eventSubscribeScriptContext( eventsub_t eventSubscribeScriptContext(
event_t *event, event_t *event,
scriptmanager_t *context, scriptcontext_t *context,
jerry_value_t funcValue const int functionIndex
); );
/** /**
@@ -101,10 +101,7 @@ void eventUnsubscribe(event_t *event, const eventsub_t subscription);
* @param event The event to unsubscribe from. * @param event The event to unsubscribe from.
* @param context The script context whose listeners should be removed. * @param context The script context whose listeners should be removed.
*/ */
void eventUnsubscribeScriptContext( void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx);
event_t *event,
const scriptmanager_t *ctx
);
/** /**
* Invoke an event, calling all subscribed listeners. Optionally provide event * Invoke an event, calling all subscribed listeners. Optionally provide event
+5 -5
View File
@@ -1,13 +1,13 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "eventcallback.h" #include "eventcallback.h"
#include "script/scriptmanager.h" #include "script/scriptcontext.h"
typedef enum { typedef enum {
EVENT_TYPE_C = 0, EVENT_TYPE_C = 0,
@@ -15,8 +15,8 @@ typedef enum {
} eventtype_t; } eventtype_t;
typedef struct { typedef struct {
scriptmanager_t *context; scriptcontext_t *context;
jerry_value_t funcValue; int luaFunctionRef;
} eventscript_t; } eventscript_t;
typedef struct eventc_s { typedef struct eventc_s {
@@ -27,4 +27,4 @@ typedef struct eventc_s {
typedef union eventuserdata_u { typedef union eventuserdata_u {
eventscript_t script; eventscript_t script;
eventc_t c; eventc_t c;
} eventuserdata_t; } eventuserdata_t;
+28 -34
View File
@@ -95,27 +95,27 @@ void inputUpdate(void) {
if(TIME.dynamicUpdate) return; if(TIME.dynamicUpdate) return;
#endif #endif
// if(INPUT.eventPressed.listenerCount > 0) { if(INPUT.eventPressed.listenerCount > 0) {
// action = &INPUT.actions[0]; action = &INPUT.actions[0];
// do { do {
// if(inputPressed(action->action)) { if(inputPressed(action->action)) {
// inputevent_t inputEvent = { .action = action->action }; inputevent_t inputEvent = { .action = action->action };
// eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt"); eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt");
// } }
// action++; action++;
// } while(action < &INPUT.actions[INPUT_ACTION_COUNT]); } while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
// } }
// if(INPUT.eventReleased.listenerCount > 0) { if(INPUT.eventReleased.listenerCount > 0) {
// action = &INPUT.actions[0]; action = &INPUT.actions[0];
// do { do {
// if(inputReleased(action->action)) { if(inputReleased(action->action)) {
// inputevent_t inputEvent = { .action = action->action }; inputevent_t inputEvent = { .action = action->action };
// eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt"); eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt");
// } }
// action++; action++;
// } while(action < &INPUT.actions[INPUT_ACTION_COUNT]); } while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
// } }
} }
float_t inputGetCurrentValue(const inputaction_t action) { float_t inputGetCurrentValue(const inputaction_t action) {
@@ -171,16 +171,6 @@ float_t inputAxis(const inputaction_t neg, const inputaction_t pos) {
return inputGetCurrentValue(pos) - inputGetCurrentValue(neg); return inputGetCurrentValue(pos) - inputGetCurrentValue(neg);
} }
void inputAxis2D(
const inputaction_t negX, const inputaction_t posX,
const inputaction_t negY, const inputaction_t posY,
vec2 result
) {
assertNotNull(result, "Result vector cannot be null");
result[0] = inputAxis(negX, posX);
result[1] = inputAxis(negY, posY);
}
void inputBind(const inputbutton_t button, const inputaction_t act) { void inputBind(const inputbutton_t button, const inputaction_t act) {
assertTrue( assertTrue(
act < INPUT_ACTION_COUNT, act < INPUT_ACTION_COUNT,
@@ -189,15 +179,19 @@ void inputBind(const inputbutton_t button, const inputaction_t act) {
assertTrue(act != INPUT_ACTION_NULL, "Cannot bind to NULL action"); assertTrue(act != INPUT_ACTION_NULL, "Cannot bind to NULL action");
// Get the button data for this button. // Get the button data for this button.
inputbuttondata_t *data = inputButtonGetData(button); inputbuttondata_t *data = INPUT_BUTTON_DATA;
assertNotNull(data, "Input button not found"); do {
if(memoryCompare(&data->button, &button, sizeof(inputbutton_t)) == 0) {
break;
}
data++;
} while(data->name != NULL);
assertNotNull(data->name, "Input button not found");
// Bind the action. // Bind the action.
data->action = act; data->action = act;
} }
float_t inputDeadzone(const float_t rawValue, const float_t deadzone) { float_t inputDeadzone(const float_t rawValue, const float_t deadzone) {
if(rawValue < deadzone) return 0.0f; if(rawValue < deadzone) return 0.0f;
return (rawValue - deadzone) / (1.0f - deadzone); return (rawValue - deadzone) / (1.0f - deadzone);
+1 -2
View File
@@ -5,7 +5,6 @@ LEFT,
RIGHT, RIGHT,
ACCEPT, ACCEPT,
CANCEL, CANCEL,
RAGEQUIT, RAGEQUIT
CONSOLE,
POINTERX, POINTERX,
POINTERY, POINTERY,
1 id id,
5 RIGHT RIGHT,
6 ACCEPT ACCEPT,
7 CANCEL CANCEL,
8 RAGEQUIT RAGEQUIT
CONSOLE
9 POINTERX POINTERX,
10 POINTERY POINTERY,
-16
View File
@@ -120,22 +120,6 @@ bool_t inputReleased(const inputaction_t action);
*/ */
float_t inputAxis(const inputaction_t neg, const inputaction_t pos); float_t inputAxis(const inputaction_t neg, const inputaction_t pos);
/**
* Gets the values of a 2D input axis, defined by two pairs of actions (negative
* and positive for each axis).
*
* @param negX The action representing the negative direction of the X axis.
* @param posX The action representing the positive direction of the X axis.
* @param negY The action representing the negative direction of the Y axis.
* @param posY The action representing the positive direction of the Y axis.
* @param result A vec2 to store the resulting axis values (-1.0f to 1.0f).
*/
void inputAxis2D(
const inputaction_t negX, const inputaction_t posX,
const inputaction_t negY, const inputaction_t posY,
vec2 result
);
/** /**
* Binds an input button to an action. * Binds an input button to an action.
* *
+9 -9
View File
@@ -9,14 +9,14 @@
#include "assert/assert.h" #include "assert/assert.h"
#include "util/string.h" #include "util/string.h"
inputaction_t inputActionGetByName(const char_t *name) { // inputaction_t inputActionGetByName(const char_t *name) {
assertNotNull(name, "name must not be NULL"); // assertNotNull(name, "name must not be NULL");
for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) { // for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
if(INPUT_ACTION_IDS[i] == NULL) continue; // if(INPUT_ACTION_IDS[i] == NULL) continue;
if(stringCompareInsensitive(INPUT_ACTION_IDS[i], name) != 0) continue; // if(stringCompareInsensitive(INPUT_ACTION_IDS[i], name) != 0) continue;
return i; // return i;
} // }
return INPUT_ACTION_COUNT; // return INPUT_ACTION_COUNT;
} // }
+1 -1
View File
@@ -26,4 +26,4 @@ typedef struct {
* @param name The name of the input action. * @param name The name of the input action.
* @return The input action, or INPUT_ACTION_COUNT if not found. * @return The input action, or INPUT_ACTION_COUNT if not found.
*/ */
inputaction_t inputActionGetByName(const char_t *name); // inputaction_t inputActionGetByName(const char_t *name);
-13
View File
@@ -9,7 +9,6 @@
#include "input.h" #include "input.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/string.h" #include "util/string.h"
#include "util/memory.h"
inputbutton_t inputButtonGetByName(const char_t *name) { inputbutton_t inputButtonGetByName(const char_t *name) {
assertNotNull(name, "name must not be NULL"); assertNotNull(name, "name must not be NULL");
@@ -27,16 +26,4 @@ inputbutton_t inputButtonGetByName(const char_t *name) {
float_t inputButtonGetValue(const inputbutton_t button) { float_t inputButtonGetValue(const inputbutton_t button) {
return inputButtonGetValuePlatform(button); return inputButtonGetValuePlatform(button);
}
inputbuttondata_t * inputButtonGetData(const inputbutton_t button) {
inputbuttondata_t *data = INPUT_BUTTON_DATA;
do {
if(memoryCompare(&data->button, &button, sizeof(inputbutton_t)) == 0) {
return data;
}
data++;
} while(data->name != NULL);
return NULL;
} }
+1 -9
View File
@@ -96,12 +96,4 @@ inputbutton_t inputButtonGetByName(const char_t *name);
* @param button The input button. * @param button The input button.
* @return The current value of the input button (0.0f to 1.0f). * @return The current value of the input button (0.0f to 1.0f).
*/ */
float_t inputButtonGetValue(const inputbutton_t button); float_t inputButtonGetValue(const inputbutton_t button);
/**
* Gets the button data for a specific input button.
*
* @param button The input button to get the data for.
* @return The button data, or NULL if not found.
*/
inputbuttondata_t * inputButtonGetData(const inputbutton_t button);
+1
View File
@@ -7,4 +7,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
network.c network.c
networkinfo.c networkinfo.c
networksocketclient.c
) )
+25
View File
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
typedef struct {
const char_t *key;
const char_t *value;
} networkhttpheader_t;
typedef struct {
} networkhttprequest_t;
void networkHTTP(
const char_t *url,
const char_t *method,
void *user,
void (*onComplete)(void *user),
void (*onError)(errorret_t error, void *user)
);
+44
View File
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "networksocketclient.h"
#include "assert/assert.h"
#include "util/memory.h"
void networkSocketClientInit(
networksocketclient_t *client,
const char_t *host,
uint16_t port,
void *user,
void (*onConnect)(void *user),
void (*onError)(errorret_t error, void *user),
void (*onDisconnect)(void *user)
) {
assertNotNull(client, "Client cannot be NULL");
assertStrLenMin(host, 1, "Host cannot be empty");
assertNotNull(onConnect, "onConnect callback cannot be NULL");
assertNotNull(onError, "onError callback cannot be NULL");
assertNotNull(onDisconnect, "onDisconnect callback cannot be NULL");
memoryZero(client, sizeof(networksocketclient_t));
client->user = user;
client->onConnect = onConnect;
client->onError = onError;
client->onDisconnect = onDisconnect;
client->state = NETWORK_SOCKET_CLIENT_STATE_CONNECTING;
// Pass to platform for implementation.
networkSocketClientPlatformInit(
client,
host,
port,
user,
onConnect,
onError,
onDisconnect
);
}
+50
View File
@@ -0,0 +1,50 @@
/**
* 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 "network/networksocketclientplatform.h"
#ifndef networkSocketClientPlatformInit
#error "Define networkSocketClientPlatformInit"
#endif
typedef enum {
NETWORK_SOCKET_CLIENT_STATE_DISCONNECTED,
NETWORK_SOCKET_CLIENT_STATE_CONNECTING,
} networksocketclientstate_t;
typedef struct networksocketclient_s {
void *user;
void (*onConnect)(void *user);
void (*onError)(errorret_t error, void *user);
void (*onDisconnect)(void *user);
networksocketclientstate_t state;
networksocketclientplatform_t platform;
errorstate_t errorState;
} networksocketclient_t;
/**
* Initializes a network socket client connection.
*
* @param client The client struct to initialize.
* @param host The hostname or IP address to connect to.
* @param port The port number to connect to.
* @param user User data to pass to callbacks.
* @param onConnect Callback for when the connection is established.
* @param onError Callback for when an error occurs.
* @param onDisconnect Callback for when the connection is disconnected.
*/
void networkSocketClientInit(
networksocketclient_t *client,
const char_t *host,
uint16_t port,
void *user,
void (*onConnect)(void *user),
void (*onError)(errorret_t error, void *user),
void (*onDisconnect)(void *user)
);
+135 -274
View File
@@ -1,5 +1,5 @@
// Copyright (c) 2026 Dominic Masters // Copyright (c) 2026 Dominic Masters
// //
// This software is released under the MIT License. // This software is released under the MIT License.
// https://opensource.org/licenses/MIT // https://opensource.org/licenses/MIT
@@ -15,16 +15,56 @@
#include "display/spritebatch/spritebatch.h" #include "display/spritebatch/spritebatch.h"
#include "display/text/text.h" #include "display/text/text.h"
#include "display/screen/screen.h" #include "display/screen/screen.h"
#include "console/console.h"
#include "util/string.h"
#include "script/scriptmanager.h"
#include "script/module/scene/modulescene.h"
scene_t SCENE; scene_t SCENE;
char_t SCENE_LOG[SCENE_LOG_SIZE];
void sceneLog(const char *fmt, ...) {
char temp[512];
// 1. Format input like printf
va_list args;
va_start(args, fmt);
vsnprintf(temp, sizeof(temp), fmt, args);
va_end(args);
printf("%s", temp);
// 2. Split into lines
char *lines[64];
int line_count = 0;
char *ptr = temp;
while (*ptr && line_count < 64) {
lines[line_count++] = ptr;
char *nl = strchr(ptr, '\n');
if (!nl) break;
*nl = '\0';
ptr = nl + 1;
}
// 3. Prepend lines in reverse order (so final order is correct)
for (int i = 0; i < line_count; i++) {
char new_log[SCENE_LOG_SIZE];
snprintf(new_log, sizeof(new_log), "%s\n%s", lines[i], SCENE_LOG);
// Copy back safely
strncpy(SCENE_LOG, new_log, SCENE_LOG_SIZE - 1);
SCENE_LOG[SCENE_LOG_SIZE - 1] = '\0';
}
}
errorret_t sceneInit(void) { errorret_t sceneInit(void) {
memoryZero(&SCENE, sizeof(scene_t)); memoryZero(&SCENE, sizeof(scene_t));
SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
memoryZero(SCENE_LOG, sizeof(SCENE_LOG));
sceneLog("Init\n");
errorOk(); errorOk();
} }
@@ -34,30 +74,32 @@ errorret_t sceneUpdate(void) {
errorOk(); errorOk();
} }
#endif #endif
if(stringCompare(SCENE.sceneNext, SCENE.sceneCurrent) != 0) {
errorChain(sceneSetImmediate(SCENE.sceneNext));
}
if(SCENE.sceneActive) {
errorChain(moduleSceneCall("update"));
}
errorOk(); errorOk();
} }
dusktimeepoch_t LAST;
errorret_t sceneRender(void) { errorret_t sceneRender(void) {
// Get Cameras
entityid_t camEnts[ENTITY_COUNT_MAX]; entityid_t camEnts[ENTITY_COUNT_MAX];
componentid_t camComps[ENTITY_COUNT_MAX]; componentid_t camComps[ENTITY_COUNT_MAX];
entityid_t camCount = componentGetEntitiesWithComponent( entityid_t camCount = componentGetEntitiesWithComponent(
COMPONENT_TYPE_CAMERA, camEnts, camComps COMPONENT_TYPE_CAMERA, camEnts, camComps
); );
if(camCount == 0) errorOk();
// Get meshes
entityid_t meshEnts[ENTITY_COUNT_MAX];
componentid_t meshComps[ENTITY_COUNT_MAX];
entityid_t meshCount = componentGetEntitiesWithComponent(
COMPONENT_TYPE_MESH, meshEnts, meshComps
);
if(meshCount == 0) errorOk();
// Prep Matricies
mat4 view, proj, model; mat4 view, proj, model;
errorChain(shaderBind(&SHADER_UNLIT));
// For each camera // For each camera.
for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) { for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) {
entityid_t camEnt = camEnts[camIndex]; entityid_t camEnt = camEnts[camIndex];
componentid_t camComp = camComps[camIndex]; componentid_t camComp = camComps[camIndex];
@@ -70,271 +112,90 @@ errorret_t sceneRender(void) {
entityCameraGetProjection(camEnt, camComp, proj); entityCameraGetProjection(camEnt, camComp, proj);
entityPositionGetTransform(camEnt, camPos, view); entityPositionGetTransform(camEnt, camPos, view);
// For each entity // For each mesh.
for(entityid_t entityId = 0; entityId < ENTITY_COUNT_MAX; entityId++) { for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) {
// Does this entity have a material? entityid_t meshEnt = meshEnts[meshIndex];
componentid_t matComp = entityGetComponent(
entityId, COMPONENT_TYPE_MATERIAL
);
if(matComp != 0xFF) {
// Yes, get the mesh
componentid_t meshComp = entityGetComponent(
entityId, COMPONENT_TYPE_MESH
);
if(meshComp == 0xFF) { componentid_t meshComp = meshComps[meshIndex];
logError("Entity with material component without mesh component found\n"); mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp);
continue; if(mesh == NULL) {
}
// Yes, get the material and shader.
shadermaterial_t *material = entityMaterialGetShaderMaterial(
entityId, matComp
);
shader_t *shader = entityMaterialGetShader(entityId, matComp);
if(shader == NULL) {
logError("Entity with material component without shader found\n");
continue;
}
// Get the mesh
mesh_t *mesh = entityMeshGetMesh(entityId, meshComp);
if(mesh == NULL) {
logError("Entity with material component without mesh found\n");
continue;
}
// Get the transform.
componentid_t meshPos = entityGetComponent(
entityId, COMPONENT_TYPE_POSITION
);
if(meshPos == 0xFF) {
glm_mat4_identity(model);
} else {
entityPositionGetTransform(entityId, meshPos, model);
}
// Render the mesh.
errorChain(shaderBind(shader));
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj));
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view));
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model));
errorChain(shaderSetMaterial(shader, material));
errorChain(meshDraw(mesh, 0, -1));
continue; continue;
} }
// No, in future there may be other renderable types. componentid_t meshPos = entityGetComponent(
meshEnt, COMPONENT_TYPE_POSITION
);
if(meshPos == 0xFF) {
logError("Mesh entity without entity position found\n");
continue;
}
componentid_t meshMat = entityGetComponent(
meshEnt, COMPONENT_TYPE_MATERIAL
);
if(meshMat == 0xFF) {
logError("Mesh entity without material component found\n");
continue;
}
shadermaterial_t *material = entityMaterialGetShaderMaterial(
meshEnt, meshMat
);
shader_t *shader = entityMaterialGetShader(meshEnt, meshMat);
if(shader == NULL) {
logError("Mesh entity with material component without shader found\n");
continue;
}
entityPositionGetTransform(meshEnt, meshPos, model);
errorChain(shaderBind(shader));
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj));
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view));
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model));
errorChain(shaderSetMaterial(shader, material));
errorChain(meshDraw(mesh, 0, -1));
} }
} }
errorOk(); // Here is where UI will go
glm_ortho(
// if(camCount > 0) { 0.0f, SCREEN.width,
// // For each entity SCREEN.height, 0.0f,
// for(entityid_t entityId = 0; entityId < ENTITY_COUNT_MAX; entityId++) { 0.1f, 100.0f,
proj
// }
// entityid_t meshEnts[ENTITY_COUNT_MAX];
// componentid_t meshComps[ENTITY_COUNT_MAX];
// entityid_t meshCount = componentGetEntitiesWithComponent(
// COMPONENT_TYPE_MESH, meshEnts, meshComps
// );
// if(meshCount > 0) {
// errorChain(shaderBind(&SHADER_UNLIT));
// for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) {
// entityid_t camEnt = camEnts[camIndex];
// componentid_t camComp = camComps[camIndex];
// componentid_t camPos = entityGetComponent(camEnt, COMPONENT_TYPE_POSITION);
// if(camPos == 0xFF) {
// logError("Camera entity without entity position found\n");
// continue;
// }
// entityCameraGetProjection(camEnt, camComp, proj);
// entityPositionGetTransform(camEnt, camPos, view);
// for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) {
// entityid_t meshEnt = meshEnts[meshIndex];
// componentid_t meshComp = meshComps[meshIndex];
// mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp);
// if(mesh == NULL) continue;
// componentid_t meshPos = entityGetComponent(
// meshEnt, COMPONENT_TYPE_POSITION
// );
// if(meshPos == 0xFF) {
// logError("Mesh entity without entity position found\n");
// continue;
// }
// componentid_t meshMat = entityGetComponent(
// meshEnt, COMPONENT_TYPE_MATERIAL
// );
// if(meshMat == 0xFF) {
// logError("Mesh entity without material component found\n");
// continue;
// }
// shadermaterial_t *material = entityMaterialGetShaderMaterial(
// meshEnt, meshMat
// );
// shader_t *shader = entityMaterialGetShader(meshEnt, meshMat);
// if(shader == NULL) {
// logError("Mesh entity with material component without shader found\n");
// continue;
// }
// entityPositionGetTransform(meshEnt, meshPos, model);
// errorChain(shaderBind(shader));
// errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj));
// errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view));
// errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model));
// errorChain(shaderSetMaterial(shader, material));
// errorChain(meshDraw(mesh, 0, -1));
// }
// }
// }
// }
// glm_ortho(
// 0.0f, SCREEN.width,
// SCREEN.height, 0.0f,
// 0.1f, 100.0f,
// proj
// );
// glm_lookat(
// (vec3){ 0.0f, 0.0f, 1.0f },
// (vec3){ 0.0f, 0.0f, 0.0f },
// (vec3){ 0.0f, 1.0f, 0.0f },
// view
// );
// glm_mat4_identity(model);
// errorChain(shaderBind(&SHADER_UNLIT));
// errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
// errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
// errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
// {
// entityid_t sprEnts[ENTITY_COUNT_MAX];
// componentid_t sprComps[ENTITY_COUNT_MAX];
// entityid_t sprCount = componentGetEntitiesWithComponent(
// COMPONENT_TYPE_SPRITE, sprEnts, sprComps
// );
// for(entityid_t si = 0; si < sprCount; si++) {
// entitysprite_t *spr = entitySpriteGet(sprEnts[si], sprComps[si]);
// vec3 pos = { 0.0f, 0.0f, 0.0f };
// componentid_t posComp = entityGetComponent(
// sprEnts[si], COMPONENT_TYPE_POSITION
// );
// if(posComp != 0xFF) {
// entityPositionGetPosition(sprEnts[si], posComp, pos);
// }
// errorChain(shaderSetTexture(
// &SHADER_UNLIT, SHADER_UNLIT_TEXTURE, spr->texture
// ));
// #if !MESH_ENABLE_COLOR
// errorChain(shaderSetColor(
// &SHADER_UNLIT, SHADER_UNLIT_COLOR, spr->color
// ));
// #endif
// errorChain(spriteBatchPush(
// pos[0], pos[1],
// pos[0] + spr->width, pos[1] + spr->height,
// #if MESH_ENABLE_COLOR
// spr->color,
// #endif
// spr->uv[0], spr->uv[1],
// spr->uv[2], spr->uv[3]
// ));
// errorChain(spriteBatchFlush());
// }
// }
// errorChain(consoleDraw());
// // FPS
// char_t fpsText[32];
// dusktimeepoch_t now = timeGetEpoch();
// double_t delta = now.time - LAST.time;
// LAST = now;
// double_t fps = delta > 0 ? 1.0 / delta : 0.0;
// snprintf(fpsText, sizeof(fpsText), "FPS: %.2f", fps);
// errorChain(spriteBatchFlush());
// errorChain(textDraw(
// 0, 0,
// fpsText, COLOR_WHITE,
// &FONT_TILESET_DEFAULT, &FONT_TEXTURE_DEFAULT
// ));
// errorChain(spriteBatchFlush());
// errorOk();
}
errorret_t sceneSetImmediate(const char_t *scene) {
if(scene != SCENE.sceneNext) {
stringCopy(
SCENE.sceneNext,
scene == NULL ? "" : scene,
ASSET_FILE_PATH_MAX
);
}
if(SCENE.sceneActive) {
errorChain(moduleSceneCall("dispose"));
SCENE.sceneActive = false;
}
moduleSceneReset();
stringCopy(
SCENE.sceneCurrent,
scene == NULL ? "" : scene,
ASSET_FILE_PATH_MAX
); );
glm_lookat(
if(scene != NULL) { (vec3){ 0.0f, 0.0f, 1.0f },
jerry_value_t sceneClass = SCENE_SCRIPT_REF_NONE; (vec3){ 0.0f, 0.0f, 0.0f },
errorChain(scriptManagerExecFile(scene, &sceneClass)); (vec3){ 0.0f, 1.0f, 0.0f },
view
if(!jerry_value_is_function(sceneClass)) {
if(sceneClass != SCENE_SCRIPT_REF_NONE) jerry_value_free(sceneClass);
errorThrow("Scene '%s' must export a constructor function", scene);
}
jerry_value_t sceneObj = jerry_construct(sceneClass, NULL, 0);
jerry_value_free(sceneClass);
if(jerry_value_is_exception(sceneObj)) {
char_t errMsg[512];
moduleBaseExceptionMessage(sceneObj, errMsg, sizeof(errMsg));
jerry_value_free(sceneObj);
errorThrow("Scene '%s' constructor threw: %s", scene, errMsg);
}
SCENE.scriptRef = sceneObj;
SCENE.sceneActive = true;
}
errorOk();
}
void sceneSet(const char_t *scene) {
stringCopy(
SCENE.sceneNext,
scene == NULL ? "" : scene,
ASSET_FILE_PATH_MAX
); );
} glm_mat4_identity(model);
errorChain(shaderBind(&SHADER_UNLIT));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
// errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_0TEXTURE, &DEFAULT_FONT_TEXTURE));
// errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
errorChain(textDraw(
32, 32,
// "Hello World",
SCENE_LOG,
COLOR_WHITE,
&DEFAULT_FONT_TILESET,
&DEFAULT_FONT_TEXTURE
));
errorChain(spriteBatchFlush());
errorret_t sceneDispose(void) {
errorChain(moduleSceneCall("dispose"));
errorOk(); errorOk();
} }
errorret_t sceneSet(const char_t *script) {
errorOk();
}
void sceneDispose(void) {
}
+21 -37
View File
@@ -1,68 +1,52 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "script/scriptmanager.h" #include "error/error.h"
#include "asset/assetfile.h"
#include "event/event.h"
#define SCENE_EVENT_UPDATE_MAX 16
typedef struct { typedef struct {
bool_t sceneActive; void *nothing;
jerry_value_t scriptRef;
char_t sceneCurrent[ASSET_FILE_PATH_MAX];
char_t sceneNext[ASSET_FILE_PATH_MAX];
} scene_t; } scene_t;
extern scene_t SCENE; extern scene_t SCENE;
/** Sentinel value meaning no scene script is loaded. */ #define SCENE_LOG_SIZE 1024
#define SCENE_SCRIPT_REF_NONE ((jerry_value_t)0) extern char_t SCENE_LOG[SCENE_LOG_SIZE];
void sceneLog(const char *fmt, ...);
/** /**
* Initializes the scene manager. * Initialize the scene subsystem.
* *
* @return Any error state that happened. * @return The error return value.
*/ */
errorret_t sceneInit(void); errorret_t sceneInit(void);
/** /**
* Ticks the scene manager; may call the scene's update method. * Update the current scene.
* *
* @return Any error state that happened. * @return The error return value.
*/ */
errorret_t sceneUpdate(void); errorret_t sceneUpdate(void);
/** /**
* Renders the scene. * Render the current scene.
* *
* @return Any error state that happened. * @return The error return value.
*/ */
errorret_t sceneRender(void); errorret_t sceneRender(void);
/** /**
* Immediately switches scenes, disposing the current one first. * Set the current scene by script name.
* *
* @param scene Scene to switch to (asset file path). * @param script The script name of the scene to set.
* @return Any error state that happened.
*/ */
errorret_t sceneSetImmediate(const char_t *scene); errorret_t sceneSet(const char_t *script);
/** /**
* Requests a scene change on the next safe opportunity. * Dispose of the scene subsystem.
*
* @param scene Which scene to set.
*/ */
void sceneSet(const char_t *scene); void sceneDispose(void);
/**
* Disposes of the current scene.
*
* @return Any error state that happened.
*/
errorret_t sceneDispose(void);
+4 -2
View File
@@ -7,7 +7,9 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
scriptmanager.c scriptmanager.c
scriptproto.c scriptcontext.c
scriptmodule.c
) )
# Subdirectories # Subdirectories
add_subdirectory(module)
+14
View File
@@ -0,0 +1,14 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirectories
add_subdirectory(display)
add_subdirectory(event)
add_subdirectory(input)
add_subdirectory(locale)
add_subdirectory(system)
add_subdirectory(scene)
add_subdirectory(time)
add_subdirectory(ui)
@@ -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 "console/console.h"
static 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);
if(!jerry_value_is_boolean(args[0])) {
return moduleBaseThrow("Console.visible: expected boolean");
}
CONSOLE.visible = jerry_value_is_true(args[0]);
return jerry_undefined();
}
static void moduleConsole(void) {
scriptProtoInit(
&MODULE_CONSOLE_PROTO, "Console",
sizeof(uint8_t), NULL
);
scriptProtoDefineStaticFunc(
&MODULE_CONSOLE_PROTO, "print", moduleConsolePrint
);
scriptProtoDefineStaticProp(
&MODULE_CONSOLE_PROTO, "visible",
moduleConsoleGetVisible, moduleConsoleSetVisible
);
}
@@ -0,0 +1,18 @@
# 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
moduleglm.c
modulespritebatch.c
moduleglm.c
modulecolor.c
moduletext.c
modulescreen.c
moduletileset.c
moduletexture.c
moduleshader.c
)
@@ -0,0 +1,186 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "modulecolor.h"
#include "display/color.h"
#include "assert/assert.h"
#include "util/string.h"
#include "time/time.h"
void moduleColor(scriptcontext_t *context) {
assertNotNull(context, "Context cannot be NULL.");
if(luaL_newmetatable(context->luaState, "color_mt")) {
// Metatable is new, set __index and __newindex
lua_pushstring(context->luaState, "__index");
lua_pushcfunction(context->luaState, moduleColorIndex);
lua_settable(context->luaState, -3);
lua_pushstring(context->luaState, "__newindex");
lua_pushcfunction(context->luaState, moduleColorNewIndex);
lua_settable(context->luaState, -3);
lua_pushstring(context->luaState, "__tostring");
lua_pushcfunction(context->luaState, moduleColorToString);
lua_settable(context->luaState, -3);
}
lua_pop(context->luaState, 1);
lua_register(context->luaState, "color", moduleColorFuncColor);
lua_register(context->luaState, "colorRainbow", moduleColorRainbow);
scriptContextExec(context, COLOR_SCRIPT);
}
int moduleColorFuncColor(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L);
assertNotNull(context, "Script context cannot be NULL.");
// Needs 4 channel uint8_t
if(
!lua_isnumber(L, 1) || !lua_isnumber(L, 2) ||
!lua_isnumber(L, 3) || !lua_isnumber(L, 4)
) {
return luaL_error(L, "color(r, g, b, a) requires four number arguments.");
}
colorchannel8_t r = (colorchannel8_t)lua_tonumber(L, 1);
colorchannel8_t g = (colorchannel8_t)lua_tonumber(L, 2);
colorchannel8_t b = (colorchannel8_t)lua_tonumber(L, 3);
colorchannel8_t a = (colorchannel8_t)lua_tonumber(L, 4);
// Create color_t as lua memory, and push metatable
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
luaL_getmetatable(L, "color_mt");
lua_setmetatable(L, -2);
// Initial values.
color->r = r;
color->g = g;
color->b = b;
color->a = a;
return 1;
}
int moduleColorIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
const char_t *key = lua_tostring(L, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
const color_t *color = (const color_t*)luaL_checkudata(L, 1, "color_mt");
assertNotNull(color, "Color struct cannot be NULL.");
if(stringCompare(key, "r") == 0) {
lua_pushnumber(L, color->r);
return 1;
}
if(stringCompare(key, "g") == 0) {
lua_pushnumber(L, color->g);
return 1;
}
if(stringCompare(key, "b") == 0) {
lua_pushnumber(L, color->b);
return 1;
}
if(stringCompare(key, "a") == 0) {
lua_pushnumber(L, color->a);
return 1;
}
lua_pushnil(L);
return 1;
}
int moduleColorNewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
const char_t *key = lua_tostring(L, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt");
assertNotNull(color, "Color struct cannot be NULL.");
if(stringCompare(key, "r") == 0) {
if(!lua_isnumber(L, 3)) {
return luaL_error(L, "color channel r must be a number.");
}
color->r = (colorchannel8_t)lua_tonumber(L, 3);
return 0;
} else if(stringCompare(key, "g") == 0) {
if(!lua_isnumber(L, 3)) {
return luaL_error(L, "color channel g must be a number.");
}
color->g = (colorchannel8_t)lua_tonumber(L, 3);
return 0;
} else if(stringCompare(key, "b") == 0) {
if(!lua_isnumber(L, 3)) {
return luaL_error(L, "color channel b must be a number.");
}
color->b = (colorchannel8_t)lua_tonumber(L, 3);
return 0;
} else if(stringCompare(key, "a") == 0) {
if(!lua_isnumber(L, 3)) {
return luaL_error(L, "color channel a must be a number.");
}
color->a = (colorchannel8_t)lua_tonumber(L, 3);
return 0;
}
return 0;
}
int moduleColorToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
const color_t *color = (const color_t*)luaL_checkudata(L, 1, "color_mt");
assertNotNull(color, "Color struct cannot be NULL.");
lua_pushfstring(
L, "color(r=%d, g=%d, b=%d, a=%d)",
color->r, color->g, color->b, color->a
);
return 1;
}
int moduleColorRainbow(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
// Allow time offset
float_t t = TIME.time;
if(lua_gettop(L) >= 1) {
if(!lua_isnumber(L, 1)) {
return luaL_error(L, "Rainbow time offset must be a number.");
}
t += (float_t)lua_tonumber(L, 1);
}
// Allow speed multiplier
if(lua_gettop(L) >= 2) {
if(!lua_isnumber(L, 2)) {
return luaL_error(L, "Rainbow speed multiplier must be a number.");
}
t *= (float_t)lua_tonumber(L, 2);
}
// Generate rainbow based on time.
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
color->r = (colorchannel8_t)((sinf(t) + 1.0f) * 0.5f * 255.0f);
color->g = (colorchannel8_t)((sinf(t + 2.0f) + 1.0f) * 0.5f * 255.0f);
color->b = (colorchannel8_t)((sinf(t + 4.0f) + 1.0f) * 0.5f * 255.0f);
color->a = 255;
// Set metatable
luaL_getmetatable(L, "color_mt");
lua_setmetatable(L, -2);
return 1;
}
+42 -145
View File
@@ -1,158 +1,55 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/scriptcontext.h"
#include "display/color.h"
#include "time/time.h"
#include "script/scriptproto.h"
static scriptproto_t MODULE_COLOR_PROTO; /**
* Registers the color module with the given script context.
*
* @param context The script context to register the module with.
*/
void moduleColor(scriptcontext_t *context);
static inline color_t * moduleColorGet( /**
const jerry_call_info_t *callInfo * Lua function to create a color.
) { *
return (color_t*)scriptProtoGetValue( * @param L The Lua state.
&MODULE_COLOR_PROTO, callInfo->this_value * @return Number of return values.
); */
} int moduleColorFuncColor(lua_State *L);
moduleBaseFunction(moduleColorGetR) { /**
color_t *c = moduleColorGet(callInfo); * Index function for the color structure.
return c ? jerry_number(c->r) : jerry_undefined(); * @param L The Lua state.
} * @return Number of return values.
*/
int moduleColorIndex(lua_State *L);
moduleBaseFunction(moduleColorGetG) { /**
color_t *c = moduleColorGet(callInfo); * New index function for the color structure.
return c ? jerry_number(c->g) : jerry_undefined(); *
} * @param L The Lua state.
* @return Number of return values.
*/
int moduleColorNewIndex(lua_State *L);
moduleBaseFunction(moduleColorGetB) { /**
color_t *c = moduleColorGet(callInfo); * Color to string method for script
return c ? jerry_number(c->b) : jerry_undefined(); *
} * @param L The Lua state.
* @return Number of return values.
*/
int moduleColorToString(lua_State *L);
moduleBaseFunction(moduleColorGetA) { /**
color_t *c = moduleColorGet(callInfo); * Lua function to create a rainbow color based on time.
return c ? jerry_number(c->a) : jerry_undefined(); *
} * @param L The Lua state.
* @return Number of return values.
moduleBaseFunction(moduleColorSetR) { */
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0); int moduleColorRainbow(lua_State *L);
color_t *c = moduleColorGet(callInfo);
if(!c) return jerry_undefined();
c->r = (colorchannel8_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleColorSetG) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
color_t *c = moduleColorGet(callInfo);
if(!c) return jerry_undefined();
c->g = (colorchannel8_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleColorSetB) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
color_t *c = moduleColorGet(callInfo);
if(!c) return jerry_undefined();
c->b = (colorchannel8_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleColorSetA) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
color_t *c = moduleColorGet(callInfo);
if(!c) return jerry_undefined();
c->a = (colorchannel8_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleColorToString) {
color_t *c = moduleColorGet(callInfo);
if(!c) return jerry_undefined();
char_t buf[64];
stringFormat(
buf, sizeof(buf),
"{ \"r\": %d, \"g\": %d, \"b\": %d, \"a\": %d }",
(int32_t)c->r, (int32_t)c->g,
(int32_t)c->b, (int32_t)c->a
);
return jerry_string_sz(buf);
}
static jerry_value_t moduleColorMakeObject(color_t color) {
return scriptProtoCreateValue(&MODULE_COLOR_PROTO, &color);
}
moduleBaseFunction(moduleColorConstructor) {
if(argc > 0 && !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Color: r must be a number");
}
if(argc > 1 && !jerry_value_is_number(args[1])) {
return moduleBaseThrow("Color: g must be a number");
}
if(argc > 2 && !jerry_value_is_number(args[2])) {
return moduleBaseThrow("Color: b must be a number");
}
if(argc > 3 && !jerry_value_is_number(args[3])) {
return moduleBaseThrow("Color: a must be a number");
}
color_t c;
c.r = argc > 0 ? (colorchannel8_t)jerry_value_as_number(args[0]) : 255;
c.g = argc > 1 ? (colorchannel8_t)jerry_value_as_number(args[1]) : 255;
c.b = argc > 2 ? (colorchannel8_t)jerry_value_as_number(args[2]) : 255;
c.a = argc > 3 ? (colorchannel8_t)jerry_value_as_number(args[3]) : 255;
return moduleColorMakeObject(c);
}
moduleBaseFunction(moduleColorRainbow) {
float_t t;
if(argc >= 1 && jerry_value_is_number(args[0])) {
t = (float_t)jerry_value_as_number(args[0]);
} else {
t = TIME.time * 4.0f;
}
if(argc >= 2 && jerry_value_is_number(args[1])) {
t *= (float_t)jerry_value_as_number(args[1]);
}
color_t c;
c.r = (colorchannel8_t)((sinf(t) + 1.0f) * 0.5f * 255.0f);
c.g = (colorchannel8_t)((sinf(t + 2.0f) + 1.0f) * 0.5f * 255.0f);
c.b = (colorchannel8_t)((sinf(t + 4.0f) + 1.0f) * 0.5f * 255.0f);
c.a = 255;
return moduleColorMakeObject(c);
}
static void moduleColor(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
);
scriptProtoDefineStaticFunc(
&MODULE_COLOR_PROTO, "rainbow", moduleColorRainbow
);
scriptProtoDefineToString(&MODULE_COLOR_PROTO, moduleColorToString);
moduleBaseEval(COLOR_SCRIPT);
}
+283
View File
@@ -0,0 +1,283 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleglm.h"
#include "assert/assert.h"
#include "util/string.h"
#include "util/memory.h"
void moduleGLM(scriptcontext_t *context) {
assertNotNull(context, "Context cannot be NULL.");
// Create metatable for vec3 structure.
if(luaL_newmetatable(context->luaState, "vec3_mt")) {
// Metatable methods
lua_pushcfunction(context->luaState, moduleVec3Index);
lua_setfield(context->luaState, -2, "__index");
lua_pushcfunction(context->luaState, moduleVec3NewIndex);
lua_setfield(context->luaState, -2, "__newindex");
lua_pushcfunction(context->luaState, moduleVec3ToString);
lua_setfield(context->luaState, -2, "__tostring");
}
lua_pop(context->luaState, 1);
if(luaL_newmetatable(context->luaState, "vec4_mt")) {
// Metatable methods
lua_pushcfunction(context->luaState, moduleVec4Index);
lua_setfield(context->luaState, -2, "__index");
lua_pushcfunction(context->luaState, moduleVec4NewIndex);
lua_setfield(context->luaState, -2, "__newindex");
lua_pushcfunction(context->luaState, moduleVec4ToString);
lua_setfield(context->luaState, -2, "__tostring");
}
lua_pop(context->luaState, 1);
lua_register(context->luaState, "vec3", moduleVec3Create);
}
int moduleVec3Create(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
memoryZero(v, sizeof(vec3));
// May be expecting between 1 and 3 values.
int top = lua_gettop(l);
if(top >= 1) {
if(!lua_isnumber(l, 1)) {
luaL_error(l, "Vec3 x component must be a number.");
}
(*v)[0] = (float_t)lua_tonumber(l, 1);
}
if(top >= 2) {
if(!lua_isnumber(l, 2)) {
luaL_error(l, "Vec3 y component must be a number.");
}
(*v)[1] = (float_t)lua_tonumber(l, 2);
}
if(top >= 3) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec3 z component must be a number.");
}
(*v)[2] = (float_t)lua_tonumber(l, 3);
}
// Set metatable
luaL_getmetatable(l, "vec3_mt");
lua_setmetatable(l, -2);
return 1;
}
int moduleVec3Index(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
const char_t *key = lua_tostring(l, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt");
assertNotNull(vec, "Vec3 pointer cannot be NULL.");
if(stringCompare(key, "x") == 0) {
lua_pushnumber(l, (*vec)[0]);
return 1;
} else if(stringCompare(key, "y") == 0) {
lua_pushnumber(l, (*vec)[1]);
return 1;
} else if(stringCompare(key, "z") == 0) {
lua_pushnumber(l, (*vec)[2]);
return 1;
}
}
int moduleVec3NewIndex(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
const char_t *key = luaL_checkstring(l, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt");
assertNotNull(vec, "Vec3 pointer cannot be NULL.");
if(stringCompare(key, "x") == 0) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec3 x component must be a number.");
}
(*vec)[0] = (float_t)lua_tonumber(l, 3);
return 0;
} else if(stringCompare(key, "y") == 0) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec3 y component must be a number.");
}
(*vec)[1] = (float_t)lua_tonumber(l, 3);
return 0;
} else if(stringCompare(key, "z") == 0) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec3 z component must be a number.");
}
(*vec)[2] = (float_t)lua_tonumber(l, 3);
return 0;
}
luaL_error(l, "Invalid key for vec3: %s", key);
return 0;
}
int moduleVec3ToString(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt");
assertNotNull(vec, "Vec3 pointer cannot be NULL.");
char buf[128];
snprintf(
buf, sizeof(buf),
"vec3(%.3f, %.3f, %.3f)",
(*vec)[0], (*vec)[1], (*vec)[2]
);
lua_pushstring(l, buf);
return 1;
}
int moduleVec4Create(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
vec4 *v = (vec4 *)lua_newuserdata(l, sizeof(vec4));
memoryZero(v, sizeof(vec4));
// May be expecting between 1 and 4 values.
int top = lua_gettop(l);
if(top >= 1) {
if(!lua_isnumber(l, 1)) {
luaL_error(l, "Vec4 x component must be a number.");
}
(*v)[0] = (float_t)lua_tonumber(l, 1);
}
if(top >= 2) {
if(!lua_isnumber(l, 2)) {
luaL_error(l, "Vec4 y component must be a number.");
}
(*v)[1] = (float_t)lua_tonumber(l, 2);
}
if(top >= 3) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec4 z component must be a number.");
}
(*v)[2] = (float_t)lua_tonumber(l, 3);
}
if(top >= 4) {
if(!lua_isnumber(l, 4)) {
luaL_error(l, "Vec4 w component must be a number.");
}
(*v)[3] = (float_t)lua_tonumber(l, 4);
}
// Set metatable
luaL_getmetatable(l, "vec4_mt");
lua_setmetatable(l, -2);
return 1;
}
int moduleVec4Index(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
const char_t *key = lua_tostring(l, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt");
assertNotNull(vec, "Vec4 pointer cannot be NULL.");
if(stringCompare(key, "x") == 0) {
lua_pushnumber(l, (*vec)[0]);
return 1;
} else if(stringCompare(key, "y") == 0) {
lua_pushnumber(l, (*vec)[1]);
return 1;
} else if(stringCompare(key, "z") == 0) {
lua_pushnumber(l, (*vec)[2]);
return 1;
} else if(stringCompare(key, "w") == 0) {
lua_pushnumber(l, (*vec)[3]);
return 1;
} else if(stringCompare(key, "u0") == 0) {
lua_pushnumber(l, (*vec)[0]);
return 1;
} else if(stringCompare(key, "v0") == 0) {
lua_pushnumber(l, (*vec)[1]);
return 1;
} else if(stringCompare(key, "u1") == 0) {
lua_pushnumber(l, (*vec)[2]);
return 1;
} else if(stringCompare(key, "v1") == 0) {
lua_pushnumber(l, (*vec)[3]);
return 1;
}
lua_pushnil(l);
return 1;
}
int moduleVec4NewIndex(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
const char_t *key = luaL_checkstring(l, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt");
assertNotNull(vec, "Vec4 pointer cannot be NULL.");
if(stringCompare(key, "x") == 0) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec4 x component must be a number.");
}
(*vec)[0] = (float_t)lua_tonumber(l, 3);
return 0;
} else if(stringCompare(key, "y") == 0) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec4 y component must be a number.");
}
(*vec)[1] = (float_t)lua_tonumber(l, 3);
return 0;
} else if(stringCompare(key, "z") == 0) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec4 z component must be a number.");
}
(*vec)[2] = (float_t)lua_tonumber(l, 3);
return 0;
} else if(stringCompare(key, "w") == 0) {
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Vec4 w component must be a number.");
}
(*vec)[3] = (float_t)lua_tonumber(l, 3);
return 0;
}
luaL_error(l, "Invalid key for vec4: %s", key);
return 0;
}
int moduleVec4ToString(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt");
assertNotNull(vec, "Vec4 pointer cannot be NULL.");
char buf[128];
snprintf(
buf, sizeof(buf),
"vec4(%.3f, %.3f, %.3f, %.3f)",
(*vec)[0], (*vec)[1], (*vec)[2], (*vec)[3]
);
lua_pushstring(l, buf);
return 1;
}
@@ -0,0 +1,80 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the GLM module with the given script context.
*
* @param context The script context to register the module with.
*/
void moduleGLM(scriptcontext_t *context);
/**
* Creates a new vec3 structure in Lua.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleVec3Create(lua_State *l);
/**
* Lua __index metamethod for vec3 structure.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleVec3Index(lua_State *l);
/**
* Lua __newindex metamethod for vec3 structure.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleVec3NewIndex(lua_State *l);
/**
* Lua __tostring metamethod for vec3 structure.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleVec3ToString(lua_State *l);
/**
* Creates a new vec4 structure in Lua.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleVec4Create(lua_State *l);
/**
* Lua __index metamethod for vec4 structure.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleVec4Index(lua_State *l);
/**
* Lua __newindex metamethod for vec4 structure.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleVec4NewIndex(lua_State *l);
/**
* Lua __tostring metamethod for vec4 structure.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleVec4ToString(lua_State *l);
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "modulescreen.h"
#include "assert/assert.h"
#include "display/screen/screen.h"
void moduleScreen(scriptcontext_t *context) {
assertNotNull(context, "Context cannot be NULL.");
lua_register(context->luaState, "screenGetWidth", moduleScreenGetWidth);
lua_register(context->luaState, "screenGetHeight", moduleScreenGetHeight);
lua_register(context->luaState, "screenSetBackground", moduleScreenSetBackground);
}
int moduleScreenGetWidth(lua_State *L) {
assertNotNull(L, "Lua state is null");
lua_pushnumber(L, SCREEN.width);
return 1;
}
int moduleScreenGetHeight(lua_State *L) {
assertNotNull(L, "Lua state is null");
lua_pushnumber(L, SCREEN.height);
return 1;
}
int moduleScreenSetBackground(lua_State *L) {
assertNotNull(L, "Lua state is null");
if(!lua_isuserdata(L, 1)) {
luaL_error(L, "Screen background color must be a color struct");
return 0;
}
color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt");
SCREEN.background = *color;
return 0;
}
+29 -63
View File
@@ -1,74 +1,40 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "script/module/display/modulecolor.h" #include "script/scriptcontext.h"
#include "display/screen/screen.h"
static scriptproto_t MODULE_SCREEN_PROTO; /**
* Registers the Screen module with the given script context.
*
* @param context The script context to register the module with.
*/
void moduleScreen(scriptcontext_t *context);
moduleBaseFunction(moduleScreenGetWidth) { /**
return jerry_number(SCREEN.width); * Gets the current screen width.
} *
* @param L The Lua state.
* @return Count of return values.
*/
int moduleScreenGetWidth(lua_State *L);
moduleBaseFunction(moduleScreenGetHeight) { /**
return jerry_number(SCREEN.height); * Gets the current screen height.
} *
* @param L The Lua state.
* @return Count of return values.
*/
int moduleScreenGetHeight(lua_State *L);
moduleBaseFunction(moduleScreenGetAspect) { /**
return jerry_number(SCREEN.aspect); * Sets the screen background color.
} *
* @param L The Lua state.
moduleBaseFunction(moduleScreenGetBackground) { * @return Count of return values.
return moduleColorMakeObject(SCREEN.background); */
} int moduleScreenSetBackground(lua_State *L);
moduleBaseFunction(moduleScreenSetBackground) {
if(argc < 1 || !jerry_value_is_object(args[0])) {
return moduleBaseThrow("Screen background color must be a color object");
}
color_t *color = (color_t*)scriptProtoGetValue(
&MODULE_COLOR_PROTO, args[0]
);
if(!color) {
return moduleBaseThrow("Background must be a valid color object");
}
memoryCopy(&SCREEN.background, color, sizeof(color_t));
return jerry_undefined();
}
moduleBaseFunction(moduleScreenToString) {
char_t buf[128];
stringFormat(
buf, sizeof(buf),
"{ \"width\": %d, \"height\": %d, \"aspect\": %.2f }",
SCREEN.width, SCREEN.height, SCREEN.aspect
);
return jerry_string_sz(buf);
}
static void moduleScreen(void) {
scriptProtoInit(
&MODULE_SCREEN_PROTO, "Screen", sizeof(screen_t), NULL
);
scriptProtoDefineProp(
&MODULE_SCREEN_PROTO, "width", moduleScreenGetWidth, NULL
);
scriptProtoDefineProp(
&MODULE_SCREEN_PROTO, "height", moduleScreenGetHeight, NULL
);
scriptProtoDefineProp(
&MODULE_SCREEN_PROTO, "aspect", moduleScreenGetAspect, NULL
);
scriptProtoDefineProp(
&MODULE_SCREEN_PROTO, "background",
moduleScreenGetBackground, moduleScreenSetBackground
);
scriptProtoDefineToString(&MODULE_SCREEN_PROTO, moduleScreenToString);
}
@@ -0,0 +1,119 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleshader.h"
#include "assert/assert.h"
#include "display/shader/shader.h"
#include "display/shader/shaderunlit.h"
void moduleShader(scriptcontext_t *context) {
assertNotNull(context, "Context cannot be NULL.");
// Shader unlit defs
lua_pushlightuserdata(context->luaState, &SHADER_UNLIT);
lua_setglobal(context->luaState, "SHADER_UNLIT");
lua_pushstring(context->luaState, SHADER_UNLIT_PROJECTION);
lua_setglobal(context->luaState, "SHADER_UNLIT_PROJECTION");
lua_pushstring(context->luaState, SHADER_UNLIT_VIEW);
lua_setglobal(context->luaState, "SHADER_UNLIT_VIEW");
lua_pushstring(context->luaState, SHADER_UNLIT_MODEL);
lua_setglobal(context->luaState, "SHADER_UNLIT_MODEL");
lua_pushstring(context->luaState, SHADER_UNLIT_TEXTURE);
lua_setglobal(context->luaState, "SHADER_UNLIT_TEXTURE");
// Shader methods
lua_register(context->luaState, "shaderBind", moduleShaderBind);
lua_register(context->luaState, "shaderSetMatrix", moduleShaderSetMatrix);
lua_register(context->luaState, "shaderSetTexture", moduleShaderSetTexture);
}
int moduleShaderBind(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
// Should be passed a shader userdata pointer only.
shader_t *shader = (shader_t *)lua_touserdata(l, 1);
assertNotNull(shader, "Shader pointer cannot be NULL.");
errorret_t ret = shaderBind(shader);
if(ret.code != ERROR_OK) {
luaL_error(l, "Failed to bind shader: %s", ret.state->message);
errorCatch(errorPrint(ret));
return 0;
}
return 0;
}
int moduleShaderSetMatrix(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
// Expect shader, string and matrix.
if(!lua_isuserdata(l, 1)) {
luaL_error(l, "First argument must be a shader_mt userdata.");
return 0;
}
if(!lua_isstring(l, 2)) {
luaL_error(l, "Second argument must be a string.");
return 0;
}
if(!lua_isuserdata(l, 3)) {
luaL_error(l, "Third argument must be a mat4_mt userdata.");
return 0;
}
shader_t *shader = (shader_t *)lua_touserdata(l, 1);
assertNotNull(shader, "Shader pointer cannot be NULL.");
const char_t *uniformName = luaL_checkstring(l, 2);
assertStrLenMin(uniformName, 1, "Uniform name cannot be empty.");
mat4 *mat = (mat4 *)lua_touserdata(l, 3);
assertNotNull(mat, "Matrix pointer cannot be NULL.");
errorret_t ret = shaderSetMatrix(shader, uniformName, *mat);
if(ret.code != ERROR_OK) {
luaL_error(l, "Failed to set shader matrix: %s", ret.state->message);
errorCatch(errorPrint(ret));
return 0;
}
return 0;
}
int moduleShaderSetTexture(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
shader_t *shader = (shader_t *)lua_touserdata(l, 1);
assertNotNull(shader, "Shader pointer cannot be NULL.");
const char_t *uniformName = luaL_checkstring(l, 2);
assertStrLenMin(uniformName, 1, "Uniform name cannot be empty.");
texture_t *texture;
// Texture can be Nil or a pointer, if not nil it must be a texture pointer.
if(lua_isnil(l, 3)) {
texture = NULL;
} else if(lua_isuserdata(l, 3)) {
texture = (texture_t *)lua_touserdata(l, 3);
assertNotNull(texture, "Texture pointer cannot be NULL.");
} else {
luaL_error(l, "Third argument must be a texture_mt userdata or nil.");
return 0;
}
errorret_t ret = shaderSetTexture(shader, uniformName, texture);
if(ret.code != ERROR_OK) {
luaL_error(l, "Failed to set shader texture: %s", ret.state->message);
errorCatch(errorPrint(ret));
return 0;
}
return 0;
}
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Register shader functions to the given script context.
*
* @param context The script context to register shader functions to.
*/
void moduleShader(scriptcontext_t *context);
/**
* Script binding for binding a shader.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleShaderBind(lua_State *l);
/**
* Script binding for setting a matrix uniform in a shader.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleShaderSetMatrix(lua_State *l);
/**
* Script binding for setting a texture uniform in a shader.
*
* @param l The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleShaderSetTexture(lua_State *l);
errorret_t doThing();
@@ -0,0 +1,105 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "modulespritebatch.h"
#include "display/spritebatch/spritebatch.h"
#include "assert/assert.h"
void moduleSpriteBatch(scriptcontext_t *context) {
lua_register(context->luaState, "spriteBatchFlush", moduleSpriteBatchFlush);
lua_register(context->luaState, "spriteBatchClear", moduleSpriteBatchClear);
lua_register(context->luaState, "spriteBatchPush", moduleSpriteBatchPush);
}
int moduleSpriteBatchFlush(lua_State *L) {
assertNotNull(L, "Lua state is null");
spriteBatchFlush();
return 0;
}
int moduleSpriteBatchClear(lua_State *L) {
assertNotNull(L, "Lua state is null");
spriteBatchClear();
return 0;
}
int moduleSpriteBatchPush(lua_State *L) {
assertNotNull(L, "Lua state is null");
// MinX, MinY, MaxX, MaxY
if(
!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ||
!lua_isnumber(L, 4)
) {
return luaL_error(L, "Sprite coordinates must be numbers");
}
// Color (struct) or nil for white
color_t *color = NULL;
if(lua_gettop(L) < 5 || lua_isnil(L, 5)) {
// Allow NULL
} else if(lua_isuserdata(L, 5)) {
color = (color_t*)luaL_checkudata(L, 5, "color_mt");
} else {
return luaL_error(L, "Sprite color must be a color struct or nil");
}
// Optional UV min and maxes, defaults to 0,0 -> 1,1
float_t u0 = 0.0f;
float_t v0 = 0.0f;
float_t u1 = 1.0f;
float_t v1 = 1.0f;
if(lua_gettop(L) >= 7) {
if(!lua_isnumber(L, 6) || !lua_isnumber(L, 7)) {
return luaL_error(L, "Sprite UV min coordinates must be numbers");
}
u0 = (float_t)lua_tonumber(L, 6);
v0 = (float_t)lua_tonumber(L, 7);
}
if(lua_gettop(L) >= 9) {
if(!lua_isnumber(L, 8) || !lua_isnumber(L, 9)) {
return luaL_error(L, "Sprite UV max coordinates must be numbers");
}
u1 = (float_t)lua_tonumber(L, 8);
v1 = (float_t)lua_tonumber(L, 9);
}
float_t minX = (float_t)lua_tonumber(L, 1);
float_t minY = (float_t)lua_tonumber(L, 2);
float_t maxX = (float_t)lua_tonumber(L, 3);
float_t maxY = (float_t)lua_tonumber(L, 4);
errorret_t ret = spriteBatchPush(
minX,
minY,
maxX,
maxY,
#if MESH_ENABLE_COLOR
color == NULL ? COLOR_WHITE : *color,
#endif
u0,
v0,
u1,
v1
);
if(ret.code != ERROR_OK) {
int err = luaL_error(L,
"Failed to push sprite to batch: %s",
ret.state->message
);
errorCatch(errorPrint(ret));
return err;
}
return 0;
}
@@ -0,0 +1,40 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Register sprite batch functions to the given script context.
*
* @param context The script context to register sprite batch functions to.
*/
void moduleSpriteBatch(scriptcontext_t *context);
/**
* Script binding for flushing the sprite batch.
*
* @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleSpriteBatchFlush(lua_State *L);
/**
* Script binding for clearing the sprite batch.
*
* @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleSpriteBatchClear(lua_State *L);
/**
* Script binding for pushing a sprite to the sprite batch.
*
* @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleSpriteBatchPush(lua_State *L);
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduletext.h"
#include "assert/assert.h"
#include "display/text/text.h"
#include "display/spritebatch/spritebatch.h"
void moduleText(scriptcontext_t *context) {
assertNotNull(context, "Script context is null");
lua_register(context->luaState, "textDraw", moduleTextDraw);
lua_register(context->luaState, "textMeasure", moduleTextMeasure);
}
int moduleTextDraw(lua_State *L) {
assertNotNull(L, "Lua state is null");
// Position.
if(!lua_isnumber(L, 1)) {
return luaL_error(L, "X position must be a number");
}
if(!lua_isnumber(L, 2)) {
return luaL_error(L, "Y position must be a number");
}
const float_t x = (float_t)lua_tonumber(L, 1);
const float_t y = (float_t)lua_tonumber(L, 2);
// String
if(!lua_isstring(L, 3)) {
return luaL_error(L, "Text to draw must be a string");
}
const char_t *text = (const char_t*)lua_tostring(L, 3);
// Optional color
color_t *color = NULL;
if(lua_gettop(L) < 4 || lua_isnil(L, 4)) {
// Allow NULL
} else if(lua_isuserdata(L, 4)) {
color = (color_t*)luaL_checkudata(L, 4, "color_mt");
} else {
return luaL_error(L, "Sprite color must be a color struct or nil");
}
// For now, use the default font tileset and texture.
errorret_t ret = textDraw(
x,
y,
text,
color == NULL ? COLOR_WHITE : *color,
&DEFAULT_FONT_TILESET,
&DEFAULT_FONT_TEXTURE
);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(L, "Failed to draw text");
}
ret = spriteBatchFlush();
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(L, "Failed to flush sprite batch after drawing text");
}
}
int moduleTextMeasure(lua_State *L) {
assertNotNull(L, "Lua state is null");
// String
if(!lua_isstring(L, 1)) {
return luaL_error(L, "Text to measure must be a string");
}
const char_t *text = (const char_t*)lua_tostring(L, 1);
int32_t width = 0;
int32_t height = 0;
textMeasure(
text,
&DEFAULT_FONT_TILESET,
&width,
&height
);
lua_pushnumber(L, width);
lua_pushnumber(L, height);
return 2;
}
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Register text rendering functions to the given script context.
*
* @param context The script context to register text functions to.
*/
void moduleText(scriptcontext_t *context);
/**
* Script binding for drawing text.
*
* @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleTextDraw(lua_State *L);
/**
* Script binding for measuring text dimensions.
*
* @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleTextMeasure(lua_State *L);
@@ -0,0 +1,119 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduletexture.h"
#include "assert/assert.h"
#include "display/texture/texture.h"
#include "asset/loader/display/assettextureloader.h"
#include "util/memory.h"
#include "util/string.h"
void moduleTexture(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be null");
lua_State *l = ctx->luaState;
assertNotNull(l, "Lua state cannot be null");
// Create metatable for texture structure.
if(luaL_newmetatable(l, "texture_mt") == 1) {
lua_pushcfunction(l, moduleTextureIndex);
lua_setfield(l, -2, "__index");
lua_pushcfunction(l, moduleTextureToString);
lua_setfield(l, -2, "__tostring");
lua_pushcfunction(l, moduleTextureGC);
lua_setfield(l, -2, "__gc");
}
// Texture formats
lua_pushnumber(l, TEXTURE_FORMAT_RGBA);
lua_setglobal(l, "TEXTURE_FORMAT_RGBA");
lua_register(ctx->luaState, "textureLoad", moduleTextureLoad);
}
int moduleTextureIndex(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt");
assertNotNull(tex, "Texture pointer cannot be NULL.");
const char *key = luaL_checkstring(l, 2);
assertNotNull(key, "Key cannot be NULL.");
if(stringCompare(key, "width") == 0) {
lua_pushnumber(l, tex->width);
return 1;
} else if(stringCompare(key, "height") == 0) {
lua_pushnumber(l, tex->height);
return 1;
}
lua_pushnil(l);
return 1;
}
int moduleTextureToString(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt");
assertNotNull(tex, "Texture pointer cannot be NULL.");
char buffer[64];
snprintf(buffer, sizeof(buffer), "Texture(%dx%d)", tex->width, tex->height);
lua_pushstring(l, buffer);
return 1;
}
int moduleTextureGC(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt");
assertNotNull(tex, "Texture pointer cannot be NULL.");
textureDispose(tex);
return 0;
}
int moduleTextureLoad(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
if(!lua_isstring(l, 1)) {
luaL_error(l, "First argument must be a string filename.");
return 0;
}
if(!lua_isnumber(l, 2)) {
luaL_error(l, "Second argument must be a number format.");
return 0;
}
const char_t *filename = lua_tostring(l, 1);
assertNotNull(filename, "Filename cannot be NULL.");
assertStrLenMin(filename, 1, "Filename cannot be empty.");
// Create texture owned to lua
texture_t *tex = (texture_t *)lua_newuserdata(l, sizeof(texture_t));
memoryZero(tex, sizeof(texture_t));
textureformat_t format = (textureformat_t)lua_tonumber(l, 2);
errorret_t ret = assetTextureLoad(filename, tex, format);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(l, "Failed to load texture asset: %s", filename);
return 0;
}
// Set metatable
luaL_getmetatable(l, "texture_mt");
lua_setmetatable(l, -2);
// Return the texture
return 1;
}
@@ -0,0 +1,15 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
void moduleTexture(scriptcontext_t *ctx);
int moduleTextureIndex(lua_State *l);
int moduleTextureToString(lua_State *l);
int moduleTextureGC(lua_State *l);
int moduleTextureLoad(lua_State *l);
@@ -0,0 +1,164 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduletileset.h"
#include "assert/assert.h"
#include "display/texture/tileset.h"
#include "util/memory.h"
#include "util/string.h"
#include "asset/loader/display/assettilesetloader.h"
void moduleTileset(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
// Tileset metatable
if(luaL_newmetatable(ctx->luaState, "tileset_mt")) {
lua_pushcfunction(ctx->luaState, moduleTilesetIndex);
lua_setfield(ctx->luaState, -2, "__index");
lua_pushcfunction(ctx->luaState, moduleTilesetToString);
lua_setfield(ctx->luaState, -2, "__tostring");
}
lua_pop(ctx->luaState, 1); // Pop the metatable
lua_register(ctx->luaState, "tilesetLoad", moduleTilesetLoad);
lua_register(ctx->luaState, "tilesetTileGetUV", moduleTilesetTileGetUV);
lua_register(
ctx->luaState, "tilesetPositionGetUV", moduleTilesetPositionGetUV
);
}
int moduleTilesetIndex(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
const char_t *key = luaL_checkstring(l, 2);
assertNotNull(key, "Key cannot be NULL.");
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
assertNotNull(ts, "Tileset pointer cannot be NULL.");
if(stringCompare(key, "tileWidth") == 0) {
lua_pushnumber(l, ts->tileWidth);
return 1;
} else if(stringCompare(key, "tileHeight") == 0) {
lua_pushnumber(l, ts->tileHeight);
return 1;
} else if(stringCompare(key, "tileCount") == 0) {
lua_pushnumber(l, ts->tileCount);
return 1;
} else if(stringCompare(key, "columns") == 0) {
lua_pushnumber(l, ts->columns);
return 1;
} else if(stringCompare(key, "rows") == 0) {
lua_pushnumber(l, ts->rows);
return 1;
}
lua_pushnil(l);
return 1;
}
int moduleTilesetToString(lua_State *l) {
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
assertNotNull(ts, "Tileset pointer cannot be NULL.");
lua_pushfstring(l, "Tileset: %dx%d tile, %d columns, %d rows",
ts->tileWidth, ts->tileHeight, ts->columns, ts->rows
);
return 1;
}
int moduleTilesetTileGetUV(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
if(!lua_isuserdata(l, 1)) {
luaL_error(l, "First argument must be a tileset userdata.");
return 0;
}
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
assertNotNull(ts, "Tileset pointer cannot be NULL.");
if(!lua_isnumber(l, 2)) {
luaL_error(l, "Second arguments must be tile index.");
return 0;
}
uint16_t tileIndex = (uint16_t)lua_tonumber(l, 2);
// Create vec4 that lua owns
vec4 *uv = (vec4 *)lua_newuserdata(l, sizeof(vec4));
tilesetTileGetUV(ts, tileIndex, *uv);
// Set metatable
luaL_getmetatable(l, "vec4_mt");
lua_setmetatable(l, -2);
return 1;
}
int moduleTilesetPositionGetUV(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
if(!lua_isuserdata(l, 1)) {
luaL_error(l, "First argument must be a tileset userdata.");
return 0;
}
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
assertNotNull(ts, "Tileset pointer cannot be NULL.");
if(!lua_isnumber(l, 2)) {
luaL_error(l, "Second arguments must be column number.");
return 0;
}
uint16_t column = (uint16_t)lua_tonumber(l, 2);
if(!lua_isnumber(l, 3)) {
luaL_error(l, "Third arguments must be row number.");
return 0;
}
uint16_t row = (uint16_t)lua_tonumber(l, 3);
// Create vec4 that lua owns
vec4 *uv = (vec4 *)lua_newuserdata(l, sizeof(vec4));
tilesetPositionGetUV(ts, column, row, *uv);
// Set metatable
luaL_getmetatable(l, "vec4_mt");
lua_setmetatable(l, -2);
return 1;
}
int moduleTilesetLoad(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
if(!lua_isstring(l, 1)) {
luaL_error(l, "First argument must be a string filename.");
return 0;
}
const char_t *filename = lua_tostring(l, 1);
assertNotNull(filename, "Filename cannot be NULL.");
assertStrLenMin(filename, 1, "Filename cannot be empty.");
// Create texture owned to lua
tileset_t *tileset = (tileset_t *)lua_newuserdata(l, sizeof(tileset_t));
memoryZero(tileset, sizeof(tileset_t));
errorret_t ret = assetTilesetLoad(filename, tileset);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(l, "Failed to load tileset asset: %s", filename);
return 0;
}
// Set metatable
luaL_getmetatable(l, "tileset_mt");
lua_setmetatable(l, -2);
// Return the tileset
return 1;
}
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the tileset module in the scripting context.
*
* @param ctx The scripting context to register the module in.
*/
void moduleTileset(scriptcontext_t *ctx);
/**
* __index metamethod for tileset userdata.
*
* @param l The Lua state.
* @return The number of return values on the Lua stack.
*/
int moduleTilesetIndex(lua_State *l);
/**
* __tostring metamethod for tileset userdata.
*
* @param l The Lua state.
* @return The number of return values on the Lua stack.
*/
int moduleTilesetToString(lua_State *l);
/**
* Lua function to get the UV coordinates for a tile index in a tileset.
*
* @param l The Lua state.
* @return The number of return values on the Lua stack.
*/
int moduleTilesetTileGetUV(lua_State *l);
/**
* Lua function to get the UV coordinates for a tile position in a tileset.
*
* @param l The Lua state.
* @return The number of return values on the Lua stack.
*/
int moduleTilesetPositionGetUV(lua_State *l);
/**
* Lua function to load a tileset from a texture and tile dimensions.
*
* @param l The Lua state.
* @return The number of return values on the Lua stack.
*/
int moduleTilesetLoad(lua_State *l);
@@ -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 "engine/engine.h"
static scriptproto_t MODULE_ENGINE_PROTO;
moduleBaseFunction(moduleEngineExit) {
ENGINE.running = false;
return jerry_undefined();
}
static void moduleEngine(void) {
scriptProtoInit(
&MODULE_ENGINE_PROTO, "Engine",
sizeof(uint8_t), NULL
);
scriptProtoDefineStaticFunc(
&MODULE_ENGINE_PROTO, "exit", moduleEngineExit
);
}
@@ -1,244 +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/entity.h"
#include "entity/component/display/entitycamera.h"
#include "moduleentityposition.h"
static scriptproto_t MODULE_ENTITY_CAMERA_PROTO;
static entitycamera_t * moduleEntityCameraGet(
const jerry_call_info_t *callInfo
) {
componenthandle_t *h = scriptProtoGetValue(
&MODULE_ENTITY_CAMERA_PROTO, callInfo->this_value
);
if(!h) return NULL;
return (entitycamera_t*)componentGetData(
h->eid, h->cid, COMPONENT_TYPE_CAMERA
);
}
moduleBaseFunction(moduleEntityCameraGetZNear) {
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam) return jerry_undefined();
return jerry_number(cam->nearClip);
}
moduleBaseFunction(moduleEntityCameraGetZFar) {
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam) return jerry_undefined();
return jerry_number(cam->farClip);
}
moduleBaseFunction(moduleEntityCameraGetOrthoTop) {
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
return jerry_undefined();
}
return jerry_number(cam->orthographic.top);
}
moduleBaseFunction(moduleEntityCameraGetOrthoBottom) {
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
return jerry_undefined();
}
return jerry_number(cam->orthographic.bottom);
}
moduleBaseFunction(moduleEntityCameraGetOrthoLeft) {
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
return jerry_undefined();
}
return jerry_number(cam->orthographic.left);
}
moduleBaseFunction(moduleEntityCameraGetOrthoRight) {
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
return jerry_undefined();
}
return jerry_number(cam->orthographic.right);
}
moduleBaseFunction(moduleEntityCameraGetFov) {
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || (
cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE &&
cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
)) {
return jerry_undefined();
}
return jerry_number(cam->perspective.fov);
}
moduleBaseFunction(moduleEntityCameraGetProjectionType) {
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam) return jerry_undefined();
return jerry_number(cam->projType);
}
moduleBaseFunction(moduleEntityCameraSetZNear) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Expected a number");
}
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam) return jerry_undefined();
cam->nearClip = (float_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleEntityCameraSetZFar) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Expected a number");
}
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam) return jerry_undefined();
cam->farClip = (float_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleEntityCameraSetOrthoTop) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Expected a number");
}
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
return jerry_undefined();
}
cam->orthographic.top = (float_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleEntityCameraSetOrthoBottom) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Expected a number");
}
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
return jerry_undefined();
}
cam->orthographic.bottom = (float_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleEntityCameraSetOrthoLeft) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Expected a number");
}
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
return jerry_undefined();
}
cam->orthographic.left = (float_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleEntityCameraSetOrthoRight) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Expected a number");
}
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
return jerry_undefined();
}
cam->orthographic.right = (float_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleEntityCameraSetFov) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Expected a number");
}
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam || (
cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE &&
cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
)) {
return jerry_undefined();
}
cam->perspective.fov = (float_t)jerry_value_as_number(args[0]);
return args[0];
}
moduleBaseFunction(moduleEntityCameraSetProjectionType) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Expected a number");
}
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
if(!cam) return jerry_undefined();
int32_t projType = (int32_t)jerry_value_as_number(args[0]);
if(
projType < ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
projType > ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC
) {
return moduleBaseThrow("Invalid projection type");
}
cam->projType = (entitycameraprojectiontype_t)projType;
return args[0];
}
moduleBaseFunction(moduleEntityCameraAdd) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_CAMERA);
componenthandle_t h = { .eid = id, .cid = comp };
return scriptProtoCreateValue(&MODULE_ENTITY_CAMERA_PROTO, &h);
}
static void moduleEntityCAMERA(void) {
scriptProtoInit(
&MODULE_ENTITY_CAMERA_PROTO, NULL, sizeof(componenthandle_t), NULL
);
scriptProtoDefineProp(
&MODULE_ENTITY_CAMERA_PROTO, "zNear",
moduleEntityCameraGetZNear, moduleEntityCameraSetZNear
);
scriptProtoDefineProp(
&MODULE_ENTITY_CAMERA_PROTO, "zFar",
moduleEntityCameraGetZFar, moduleEntityCameraSetZFar
);
scriptProtoDefineProp(
&MODULE_ENTITY_CAMERA_PROTO, "orthoTop",
moduleEntityCameraGetOrthoTop, moduleEntityCameraSetOrthoTop
);
scriptProtoDefineProp(
&MODULE_ENTITY_CAMERA_PROTO, "orthoBottom",
moduleEntityCameraGetOrthoBottom, moduleEntityCameraSetOrthoBottom
);
scriptProtoDefineProp(
&MODULE_ENTITY_CAMERA_PROTO, "orthoLeft",
moduleEntityCameraGetOrthoLeft, moduleEntityCameraSetOrthoLeft
);
scriptProtoDefineProp(
&MODULE_ENTITY_CAMERA_PROTO, "orthoRight",
moduleEntityCameraGetOrthoRight, moduleEntityCameraSetOrthoRight
);
scriptProtoDefineProp(
&MODULE_ENTITY_CAMERA_PROTO, "fov",
moduleEntityCameraGetFov, moduleEntityCameraSetFov
);
scriptProtoDefineProp(
&MODULE_ENTITY_CAMERA_PROTO, "projectionType",
moduleEntityCameraGetProjectionType, moduleEntityCameraSetProjectionType
);
moduleBaseSetInt(
"CAMERA_TYPE_ORTHOGRAPHIC",
ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC
);
moduleBaseSetInt(
"CAMERA_TYPE_PERSPECTIVE",
ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE
);
}
@@ -1,91 +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/entity.h"
#include "entity/component/display/entitymaterial.h"
#include "display/color.h"
#include "moduleentityposition.h"
static scriptproto_t MODULE_ENTITY_MATERIAL_PROTO;
static entitymaterial_t * moduleEntityMaterialGet(
const jerry_call_info_t *callInfo
) {
componenthandle_t *h = scriptProtoGetValue(
&MODULE_ENTITY_MATERIAL_PROTO, callInfo->this_value
);
if(!h) return NULL;
return (entitymaterial_t*)componentGetData(
h->eid, h->cid, COMPONENT_TYPE_MATERIAL
);
}
moduleBaseFunction(moduleEntityMaterialSetColor) {
moduleBaseRequireArgs(1);
if(!jerry_value_is_object(args[0])) {
return moduleBaseThrow("expected color object");
}
entitymaterial_t *mat = moduleEntityMaterialGet(callInfo);
if(!mat) return jerry_undefined();
jerry_value_t key;
jerry_value_t v;
color_t col;
key = jerry_string_sz("r");
v = jerry_object_get(args[0], key);
jerry_value_free(key);
col.r = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
key = jerry_string_sz("g");
v = jerry_object_get(args[0], key);
jerry_value_free(key);
col.g = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
key = jerry_string_sz("b");
v = jerry_object_get(args[0], key);
jerry_value_free(key);
col.b = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
key = jerry_string_sz("a");
v = jerry_object_get(args[0], key);
jerry_value_free(key);
col.a = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
mat->material.unlit.color = col;
return jerry_undefined();
}
moduleBaseFunction(moduleEntityMaterialAdd) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MATERIAL);
componenthandle_t h = { .eid = id, .cid = comp };
return scriptProtoCreateValue(&MODULE_ENTITY_MATERIAL_PROTO, &h);
}
static void moduleEntityMATERIAL(void) {
scriptProtoInit(
&MODULE_ENTITY_MATERIAL_PROTO,
NULL,
sizeof(componenthandle_t),
NULL
);
scriptProtoDefineFunc(
&MODULE_ENTITY_MATERIAL_PROTO,
"setColor",
moduleEntityMaterialSetColor
);
}
@@ -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 "entity/entity.h"
#include "entity/component/display/entitymesh.h"
#include "moduleentityposition.h"
static scriptproto_t MODULE_ENTITY_MESH_PROTO;
static entitymesh_t * moduleEntityMeshGet(
const jerry_call_info_t *callInfo
) {
componenthandle_t *h = scriptProtoGetValue(
&MODULE_ENTITY_MESH_PROTO, callInfo->this_value
);
if(!h) return NULL;
return (entitymesh_t*)componentGetData(h->eid, h->cid, COMPONENT_TYPE_MESH);
}
moduleBaseFunction(moduleEntityMeshAdd) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MESH);
componenthandle_t h = { .eid = id, .cid = comp };
return scriptProtoCreateValue(&MODULE_ENTITY_MESH_PROTO, &h);
}
static void moduleEntityMESH(void) {
scriptProtoInit(
&MODULE_ENTITY_MESH_PROTO, NULL,
sizeof(componenthandle_t), NULL
);
}
@@ -1,183 +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/math/modulevec3ref.h"
#include "script/scriptproto.h"
#include "entity/entity.h"
#include "entity/component/physics/entityphysics.h"
#include "moduleentityposition.h"
static scriptproto_t MODULE_ENTITY_PHYSICS_PROTO;
static entityphysics_t * moduleEntityPhysicsGet(
const jerry_call_info_t *callInfo
) {
componenthandle_t *h = scriptProtoGetValue(
&MODULE_ENTITY_PHYSICS_PROTO, callInfo->this_value
);
if(!h) return NULL;
return entityPhysicsGet(h->eid, h->cid);
}
moduleBaseFunction(moduleEntityPhysicsGetVelocity) {
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
return moduleVec3RefPush(phys->velocity, NULL, NULL);
}
moduleBaseFunction(moduleEntityPhysicsSetVelocity) {
moduleBaseRequireArgs(1);
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
vec3 v;
if(!moduleVec3AnyCheck(args[0], v)) {
return moduleBaseThrow("expected Vec3");
}
glm_vec3_copy(v, phys->velocity);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPhysicsGetOnGround) {
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
return jerry_boolean(phys->onGround);
}
moduleBaseFunction(moduleEntityPhysicsGetBodyType) {
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
return jerry_number((double)phys->type);
}
moduleBaseFunction(moduleEntityPhysicsSetBodyType) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
phys->type = (physicsbodytype_t)(int32_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPhysicsApplyImpulse) {
moduleBaseRequireArgs(1);
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
if(phys->type == PHYSICS_BODY_STATIC) return jerry_undefined();
vec3 impulse;
if(!moduleVec3Check(args[0], impulse)) {
return moduleBaseThrow("expected Vec3 impulse");
}
glm_vec3_add(phys->velocity, impulse, phys->velocity);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPhysicsSetShapeCube) {
moduleBaseRequireArgs(1);
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
vec3 half;
if(!moduleVec3Check(args[0], half)) {
return moduleBaseThrow("expected Vec3 halfExtents");
}
phys->shape.type = PHYSICS_SHAPE_CUBE;
glm_vec3_copy(half, phys->shape.data.cube.halfExtents);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPhysicsSetShapeSphere) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
phys->shape.type = PHYSICS_SHAPE_SPHERE;
phys->shape.data.sphere.radius = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPhysicsSetShapeCapsule) {
moduleBaseRequireArgs(2);
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1);
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
phys->shape.type = PHYSICS_SHAPE_CAPSULE;
phys->shape.data.capsule.radius = (float_t)jerry_value_as_number(args[0]);
phys->shape.data.capsule.halfHeight =
(float_t)jerry_value_as_number(args[1]);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPhysicsSetShapePlane) {
moduleBaseRequireArgs(2); moduleBaseRequireNumber(1);
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
if(!phys) return jerry_undefined();
vec3 normal;
if(!moduleVec3Check(args[0], normal)) {
return moduleBaseThrow("expected Vec3 normal");
}
phys->shape.type = PHYSICS_SHAPE_PLANE;
glm_vec3_copy(normal, phys->shape.data.plane.normal);
phys->shape.data.plane.distance = (float_t)jerry_value_as_number(args[1]);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPhysicsAdd) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_PHYSICS);
componenthandle_t h = { .eid = id, .cid = comp };
return scriptProtoCreateValue(&MODULE_ENTITY_PHYSICS_PROTO, &h);
}
static void moduleEntityPHYSICS(void) {
scriptProtoInit(
&MODULE_ENTITY_PHYSICS_PROTO, NULL,
sizeof(componenthandle_t), NULL
);
scriptProtoDefineProp(
&MODULE_ENTITY_PHYSICS_PROTO, "velocity",
moduleEntityPhysicsGetVelocity, moduleEntityPhysicsSetVelocity
);
scriptProtoDefineProp(
&MODULE_ENTITY_PHYSICS_PROTO, "onGround",
moduleEntityPhysicsGetOnGround, NULL
);
scriptProtoDefineProp(
&MODULE_ENTITY_PHYSICS_PROTO, "bodyType",
moduleEntityPhysicsGetBodyType, moduleEntityPhysicsSetBodyType
);
scriptProtoDefineFunc(
&MODULE_ENTITY_PHYSICS_PROTO, "applyImpulse",
moduleEntityPhysicsApplyImpulse
);
scriptProtoDefineFunc(
&MODULE_ENTITY_PHYSICS_PROTO, "setShapeCube",
moduleEntityPhysicsSetShapeCube
);
scriptProtoDefineFunc(
&MODULE_ENTITY_PHYSICS_PROTO, "setShapeSphere",
moduleEntityPhysicsSetShapeSphere
);
scriptProtoDefineFunc(
&MODULE_ENTITY_PHYSICS_PROTO, "setShapeCapsule",
moduleEntityPhysicsSetShapeCapsule
);
scriptProtoDefineFunc(
&MODULE_ENTITY_PHYSICS_PROTO, "setShapePlane",
moduleEntityPhysicsSetShapePlane
);
moduleBaseSetInt("PHYSICS_BODY_STATIC", PHYSICS_BODY_STATIC);
moduleBaseSetInt("PHYSICS_BODY_DYNAMIC", PHYSICS_BODY_DYNAMIC);
moduleBaseSetInt("PHYSICS_BODY_KINEMATIC", PHYSICS_BODY_KINEMATIC);
moduleBaseSetInt("PHYSICS_SHAPE_CUBE", PHYSICS_SHAPE_CUBE);
moduleBaseSetInt("PHYSICS_SHAPE_SPHERE", PHYSICS_SHAPE_SPHERE);
moduleBaseSetInt("PHYSICS_SHAPE_CAPSULE", PHYSICS_SHAPE_CAPSULE);
moduleBaseSetInt("PHYSICS_SHAPE_PLANE", PHYSICS_SHAPE_PLANE);
}
@@ -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 "script/module/modulebase.h"
#include "script/module/math/modulevec3ref.h"
#include "script/scriptproto.h"
#include "entity/entity.h"
#include "entity/component/display/entityposition.h"
// Shared component handle struct — defined once, used by all component modules.
#ifndef COMPONENT_HANDLE_DEFINED
#define COMPONENT_HANDLE_DEFINED
typedef struct {
entityid_t eid;
componentid_t cid;
} componenthandle_t;
#endif
static scriptproto_t MODULE_ENTITY_POSITION_PROTO;
static entityposition_t * moduleEntityPositionGet(
const jerry_call_info_t *callInfo
) {
componenthandle_t *h = scriptProtoGetValue(
&MODULE_ENTITY_POSITION_PROTO, callInfo->this_value
);
if(!h) return NULL;
return entityPositionGet(h->eid, h->cid);
}
moduleBaseFunction(moduleEntityPositionGetPosition) {
entityposition_t *pos = moduleEntityPositionGet(callInfo);
if(!pos) return jerry_undefined();
return moduleVec3RefPush(
pos->position, (void(*)(void*))entityPositionRebuild, pos
);
}
moduleBaseFunction(moduleEntityPositionSetPosition) {
moduleBaseRequireArgs(1);
entityposition_t *pos = moduleEntityPositionGet(callInfo);
if(!pos) return jerry_undefined();
vec3 v;
if(!moduleVec3AnyCheck(args[0], v)) {
return moduleBaseThrow("expected Vec3");
}
glm_vec3_copy(v, pos->position);
entityPositionRebuild(pos);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPositionGetRotation) {
entityposition_t *pos = moduleEntityPositionGet(callInfo);
if(!pos) return jerry_undefined();
return moduleVec3RefPush(
pos->rotation, (void(*)(void*))entityPositionRebuild, pos
);
}
moduleBaseFunction(moduleEntityPositionSetRotation) {
moduleBaseRequireArgs(1);
entityposition_t *pos = moduleEntityPositionGet(callInfo);
if(!pos) return jerry_undefined();
vec3 v;
if(!moduleVec3AnyCheck(args[0], v)) {
return moduleBaseThrow("expected Vec3");
}
glm_vec3_copy(v, pos->rotation);
entityPositionRebuild(pos);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPositionGetScale) {
entityposition_t *pos = moduleEntityPositionGet(callInfo);
if(!pos) return jerry_undefined();
return moduleVec3RefPush(
pos->scale, (void(*)(void*))entityPositionRebuild, pos
);
}
moduleBaseFunction(moduleEntityPositionSetScale) {
moduleBaseRequireArgs(1);
entityposition_t *pos = moduleEntityPositionGet(callInfo);
if(!pos) return jerry_undefined();
vec3 v;
if(!moduleVec3AnyCheck(args[0], v)) {
return moduleBaseThrow("expected Vec3");
}
glm_vec3_copy(v, pos->scale);
entityPositionRebuild(pos);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPositionLookAt) {
moduleBaseRequireArgs(1);
entityposition_t *pos = moduleEntityPositionGet(callInfo);
if(!pos) return jerry_undefined();
vec3 target;
if(!moduleVec3AnyCheck(args[0], target)) {
return moduleBaseThrow("expected Vec3 target");
}
vec3 up = { 0.0f, 1.0f, 0.0f };
if(argc >= 2 && !moduleVec3AnyCheck(args[1], up)) {
return moduleBaseThrow("expected Vec3 up");
}
glm_lookat(pos->position, target, up, pos->transform);
entityPositionDecompose(pos);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityPositionAdd) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_POSITION);
componenthandle_t h = { .eid = id, .cid = comp };
return scriptProtoCreateValue(&MODULE_ENTITY_POSITION_PROTO, &h);
}
static void moduleEntityPOSITION(void) {
scriptProtoInit(
&MODULE_ENTITY_POSITION_PROTO, NULL,
sizeof(componenthandle_t), NULL
);
scriptProtoDefineProp(
&MODULE_ENTITY_POSITION_PROTO, "position",
moduleEntityPositionGetPosition, moduleEntityPositionSetPosition
);
scriptProtoDefineProp(
&MODULE_ENTITY_POSITION_PROTO, "rotation",
moduleEntityPositionGetRotation, moduleEntityPositionSetRotation
);
scriptProtoDefineProp(
&MODULE_ENTITY_POSITION_PROTO, "scale",
moduleEntityPositionGetScale, moduleEntityPositionSetScale
);
scriptProtoDefineFunc(
&MODULE_ENTITY_POSITION_PROTO, "lookAt",
moduleEntityPositionLookAt
);
}
@@ -1,205 +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/entity.h"
#include "entity/entitymanager.h"
#include "component/moduleentityposition.h"
#include "component/moduleentitycamera.h"
#include "component/moduleentitymesh.h"
#include "component/moduleentitymaterial.h"
#include "component/moduleentityphysics.h"
typedef struct {
entityid_t id;
} entityscript_t;
static scriptproto_t MODULE_ENTITY_PROTO;
// Getters
moduleBaseFunction(moduleEntityGetId) {
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
&MODULE_ENTITY_PROTO, callInfo->this_value
);
if(!inst) return jerry_undefined();
return jerry_number(inst->id);
}
// Getter defined for each component type
static jerry_value_t moduleEntityGetComponent(
const jerry_call_info_t *callInfo,
const jerry_value_t args[],
const jerry_length_t argc,
const componenttype_t type,
scriptproto_t *proto
) {
assertNotNull(callInfo, "Call info must not be null");
assertTrue(argc >= 0, "Argc must be non-negative");
assertTrue(
type > COMPONENT_TYPE_NULL && type < COMPONENT_TYPE_COUNT,
"Invalid component type"
);
entityid_t entityId;
componentid_t compId;
// Get the entity script data.
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
&MODULE_ENTITY_PROTO, callInfo->this_value
);
if(!inst) return jerry_undefined();
entityId = inst->id;
// Find the component ID of the requested type.
compId = entityGetComponent(entityId, type);
if(compId == 0xFF) {
return jerry_undefined();
}
componenthandle_t h = { .eid = entityId, .cid = compId };
return scriptProtoCreateValue(proto, &h);
}
#define X(enumName, type, field, init, dispose) \
moduleBaseFunction(moduleEntityGet##enumName) { \
return moduleEntityGetComponent( \
callInfo, \
args, \
argc, \
COMPONENT_TYPE_##enumName, \
&MODULE_ENTITY_##enumName##_PROTO \
); \
}
#include "entity/componentlist.h"
#undef X
moduleBaseFunction(moduleEntityToString) {
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
&MODULE_ENTITY_PROTO, callInfo->this_value
);
if(!inst) return jerry_string_sz("Entity(?)");
char_t components[128];
size_t clen = 0;
bool_t first = true;
for(componenttype_t t = 1; t < COMPONENT_TYPE_COUNT; t++) {
if(entityGetComponent(inst->id, t) == 0xFF) continue;
if(!first) {
stringCopy(components + clen, ", ", sizeof(components) - clen);
clen += 2;
}
const char_t *name = COMPONENT_DEFINITIONS[t].enumName;
stringCopy(components + clen, name, sizeof(components) - clen);
clen += strlen(name);
first = false;
}
char_t buf[256];
if(first) {
stringFormat(
buf, sizeof(buf),
"{ \"id\": %d, \"components\": [] }", inst->id
);
} else {
stringFormat(
buf, sizeof(buf),
"{ \"id\": %d, \"components\": [ %s ] }", inst->id, components
);
}
return jerry_string_sz(buf);
}
// Methods
moduleBaseFunction(moduleEntityConstructor) {
entityscript_t *inst = (entityscript_t*)memoryAllocate(
sizeof(entityscript_t)
);
inst->id = entityManagerAdd();
jerry_object_set_native_ptr(
callInfo->this_value, &MODULE_ENTITY_PROTO.info, inst
);
return jerry_undefined();
}
moduleBaseFunction(moduleEntityAddComponent) {
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("Entity.add: expected a valid component type");
}
componenttype_t type = (componenttype_t)jerry_value_as_number(args[0]);
if(type <= COMPONENT_TYPE_NULL || type >= COMPONENT_TYPE_COUNT) {
return moduleBaseThrow("Entity.add: invalid component type");
}
// Get the entity script data.
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
&MODULE_ENTITY_PROTO, callInfo->this_value
);
if(!inst) return moduleBaseThrow("Entity.add: invalid entity");
componentid_t id = entityAddComponent(inst->id, type);
return jerry_number(id);
}
moduleBaseFunction(moduleEntityDisposeMethod) {
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
&MODULE_ENTITY_PROTO, callInfo->this_value
);
if(!inst) return jerry_undefined();
entityDispose(inst->id);
return jerry_undefined();
}
static void moduleEntity(void) {
// Init the entity prototype
scriptProtoInit(
&MODULE_ENTITY_PROTO,
"Entity",
sizeof(entityscript_t),
moduleEntityConstructor
);
scriptProtoDefineToString(&MODULE_ENTITY_PROTO, moduleEntityToString);
// Entity Methods
scriptProtoDefineFunc(
&MODULE_ENTITY_PROTO, "add", moduleEntityAddComponent
);
scriptProtoDefineFunc(
&MODULE_ENTITY_PROTO, "dispose", moduleEntityDisposeMethod
);
// Entity props
scriptProtoDefineProp(
&MODULE_ENTITY_PROTO, "id", moduleEntityGetId, NULL
);
// Init component type modules.
char_t buffer[64];
#define X(enumName, type, field, iMethod, dMethod) \
moduleEntity##enumName(); \
scriptProtoDefineProp( \
&MODULE_ENTITY_PROTO, \
COMPONENT_DEFINITIONS[COMPONENT_TYPE_##enumName].name, \
moduleEntityGet##enumName, \
NULL \
); \
snprintf( \
buffer, sizeof(buffer), \
"%s = %d\n", \
#enumName, \
COMPONENT_TYPE_##enumName \
); \
moduleBaseEval(buffer);
#include "entity/componentlist.h"
#undef X
}
@@ -0,0 +1,10 @@
# 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
moduleevent.c
)
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleevent.h"
#include "event/event.h"
#include "engine/engine.h"
#include "assert/assert.h"
int moduleEventSubscribe(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
// State has user pointer to owning scriptcontext_t
scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L);
assertNotNull(context, "Script context cannot be NULL");
// Expecting event pointer
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "eventSubscribe: Expected event pointer as first argument");
return 0;
}
// Expecting callback function (Lua function)
if(!lua_isfunction(L, 2)) {
luaL_error(L, "eventSubscribe: Expected function as second argument");
return 0;
}
event_t *event = (event_t *)lua_touserdata(L, 1);
assertNotNull(event, "Event cannot be NULL");
eventsub_t id = eventSubscribeScriptContext(event, context, 2);
// Pass back to lua.
lua_pushnumber(L, id);
return 1;
}
void moduleEvent(scriptcontext_t *context) {
// Reg functions
lua_register(context->luaState, "eventSubscribe", moduleEventSubscribe);
}
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Register event functions to the given script context.
*
* @param context The script context to register event functions to.
*/
void moduleEvent(scriptcontext_t *context);
/**
* Script binding for subscribing to an event.
*/
int moduleEventSubscribe(lua_State *L);
@@ -0,0 +1,10 @@
# 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
moduleinput.c
)
+217
View File
@@ -0,0 +1,217 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleinput.h"
#include "input/input.h"
#include "assert/assert.h"
#include "util/string.h"
void moduleInput(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL");
// Setup enums.
scriptContextExec(context, INPUT_ACTION_SCRIPT);
// Input values.
scriptContextExec(context,
""
#ifdef DUSK_INPUT_KEYBOARD
"INPUT_KEYBOARD = true\n"
#endif
#ifdef DUSK_INPUT_GAMEPAD
"INPUT_GAMEPAD = true\n"
#endif
#ifdef DUSK_INPUT_POINTER
"INPUT_POINTER = true\n"
#endif
#ifdef DUSK_INPUT_TOUCH
"INPUT_TOUCH = true\n"
#endif
);
// Metatable
if(luaL_newmetatable(context->luaState, "input_mt")) {
lua_pushcfunction(context->luaState, moduleInputIndex);
lua_setfield(context->luaState, -2, "__index");
}
lua_pop(context->luaState, 1);
// Events
lua_pushlightuserdata(context->luaState, &INPUT.eventPressed);
lua_setglobal(context->luaState, "INPUT_EVENT_PRESSED");
lua_pushlightuserdata(context->luaState, &INPUT.eventReleased);
lua_setglobal(context->luaState, "INPUT_EVENT_RELEASED");
// Bind methods
lua_register(context->luaState, "inputBind", moduleInputBind);
lua_register(context->luaState, "inputIsDown", moduleInputIsDown);
lua_register(context->luaState, "inputPressed", moduleInputPressed);
lua_register(context->luaState, "inputReleased", moduleInputReleased);
lua_register(context->luaState, "inputGetValue", moduleInputGetValue);
lua_register(context->luaState, "inputAxis", moduleInputAxis);
}
int moduleInputIndex(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL");
const char_t *key = luaL_checkstring(l, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
if(stringCompare(key, "action") == 0) {
const inputevent_t *event = (const inputevent_t*)lua_touserdata(l, 1);
if(event == NULL) {
luaL_error(l, "Expected input event as first argument");
return 0;
}
lua_pushnumber(l, event->action);
return 1;
}
lua_pushnil(l);
return 1;
}
int moduleInputBind(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
// Requires action and button.
if(!lua_isstring(L, 1)) {
luaL_error(L, "inputBind: Expected button name as first argument");
return 0;
}
// Expect action ID
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inputBind: Expected action ID as second argument");
return 0;
}
const char_t *strBtn = lua_tostring(L, 1);
const inputaction_t action = (inputaction_t)lua_tonumber(L, 2);
if(strBtn == NULL || strlen(strBtn) == 0) {
luaL_error(L, "inputBind: Button name cannot be NULL or empty");
return 0;
}
// Validate action
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputBind: Invalid action ID %d with str %s", action, strBtn);
return 0;
}
// Get button by name
inputbutton_t btn = inputButtonGetByName(strBtn);
if(btn.type == INPUT_BUTTON_TYPE_NONE) {
luaL_error(L, "inputBind: Invalid button name '%s'", strBtn);
return 0;
}
inputBind(btn, action);
return 0;
}
int moduleInputIsDown(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "inputIsDown: Expected action ID as first argument");
return 0;
}
const inputaction_t action = (inputaction_t)lua_tonumber(L, 1);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputIsDown: Invalid action ID %d", action);
return 0;
}
lua_pushboolean(L, inputIsDown(action));
return 1;
}
int moduleInputPressed(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "inputPressed: Expected action ID as first argument");
return 0;
}
const inputaction_t action = (inputaction_t)lua_tonumber(L, 1);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputPressed: Invalid action ID %d", action);
return 0;
}
lua_pushboolean(L, inputPressed(action));
return 1;
}
int moduleInputReleased(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "inputReleased: Expected action ID as first argument");
return 0;
}
const inputaction_t action = (inputaction_t)lua_tonumber(L, 1);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputReleased: Invalid action ID %d", action);
return 0;
}
lua_pushboolean(L, inputReleased(action));
return 1;
}
int moduleInputGetValue(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "inputGetValue: Expected action ID as first argument");
return 0;
}
const inputaction_t action = (inputaction_t)lua_tonumber(L, 1);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputGetValue: Invalid action ID %d", action);
return 0;
}
lua_pushnumber(L, inputGetCurrentValue(action));
return 1;
}
int moduleInputAxis(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) {
luaL_error(L, "inputAxis: Expected two action IDs as arguments (neg, pos)");
return 0;
}
const inputaction_t neg = (inputaction_t)lua_tonumber(L, 1);
const inputaction_t pos = (inputaction_t)lua_tonumber(L, 2);
if(neg < INPUT_ACTION_NULL || neg >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputAxis: Invalid negative action ID %d", neg);
return 0;
}
if(pos < INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputAxis: Invalid positive action ID %d", pos);
return 0;
}
lua_pushnumber(L, inputAxis(neg, pos));
return 1;
}
+57 -147
View File
@@ -1,161 +1,71 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2025 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/scriptcontext.h"
#include "script/scriptproto.h"
#include "script/module/math/modulevec2.h"
#include "input/input.h"
static scriptproto_t MODULE_INPUT_PROTO; /**
* Register input functions to the given script context.
*
* @param context The script context to register input functions to.
*/
void moduleInput(scriptcontext_t *context);
// Static Methods /**
moduleBaseFunction(moduleInputBind) { * Getter for input structure fields.
moduleBaseRequireArgs(2); *
moduleBaseRequireString(0); * @param l The Lua state.
moduleBaseRequireNumber(1); */
int moduleInputIndex(lua_State *l);
char_t strBtn[128]; /**
moduleBaseToString(args[0], strBtn, sizeof(strBtn)); * Script binding for binding an input button to an action.
if(strBtn[0] == '\0') { *
return moduleBaseThrow("Input.bind: button name cannot be empty"); * @param L The Lua state.
} * @return Number of return values on the Lua stack.
*/
int moduleInputBind(lua_State *L);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[1]); /**
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { * Script binding for checking if an input action is currently pressed.
return moduleBaseThrow("Input.bind: invalid action"); *
} * @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleInputIsDown(lua_State *L);
inputbutton_t btn = inputButtonGetByName(strBtn); /**
if(btn.type == INPUT_BUTTON_TYPE_NONE) { * Script binding for checking if an input action was pressed this frame.
return moduleBaseThrow("Input.bind: invalid button name"); *
} * @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleInputPressed(lua_State *L);
inputBind(btn, action); /**
return jerry_undefined(); * Script binding for checking if an input action was released this frame.
} *
* @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleInputReleased(lua_State *L);
moduleBaseFunction(moduleInputIsDown) { /**
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0); * Script binding for getting the value of an input axis.
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]); *
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { * @param L The Lua state.
return moduleBaseThrow("Input.isDown: invalid action"); * @return Number of return values on the Lua stack.
} */
return jerry_boolean(inputIsDown(action)); int moduleInputGetValue(lua_State *L);
}
moduleBaseFunction(moduleInputPressed) { /**
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0); * Script binding for inputAxis.
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]); *
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { * @param L The Lua state.
return moduleBaseThrow("Input.pressed: invalid action"); * @return Number of return values on the Lua stack.
} */
return jerry_boolean(inputPressed(action)); int moduleInputAxis(lua_State *L);
}
moduleBaseFunction(moduleInputReleased) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.released: invalid action");
}
return jerry_boolean(inputReleased(action));
}
moduleBaseFunction(moduleInputGetValue) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.getValue: invalid action");
}
return jerry_number(inputGetCurrentValue(action));
}
moduleBaseFunction(moduleInputAxis) {
moduleBaseRequireArgs(2);
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1);
const inputaction_t neg = (inputaction_t)jerry_value_as_number(args[0]);
const inputaction_t pos = (inputaction_t)jerry_value_as_number(args[1]);
if(neg <= INPUT_ACTION_NULL || neg >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.axis: invalid negative action");
}
if(pos <= INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.axis: invalid positive action");
}
return jerry_number(inputAxis(neg, pos));
}
moduleBaseFunction(moduleInputAxis2D) {
moduleBaseRequireArgs(4);
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1);
moduleBaseRequireNumber(2); moduleBaseRequireNumber(3);
const inputaction_t negX = (inputaction_t)jerry_value_as_number(args[0]);
const inputaction_t posX = (inputaction_t)jerry_value_as_number(args[1]);
const inputaction_t negY = (inputaction_t)jerry_value_as_number(args[2]);
const inputaction_t posY = (inputaction_t)jerry_value_as_number(args[3]);
if(negX <= INPUT_ACTION_NULL || negX >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.axis2D: invalid negX action");
}
if(posX <= INPUT_ACTION_NULL || posX >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.axis2D: invalid posX action");
}
if(negY <= INPUT_ACTION_NULL || negY >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.axis2D: invalid negY action");
}
if(posY <= INPUT_ACTION_NULL || posY >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.axis2D: invalid posY action");
}
vec2 result;
inputAxis2D(negX, posX, negY, posY, result);
return moduleVec2Push(result);
}
static void moduleInput(void) {
moduleBaseEval(INPUT_ACTION_SCRIPT);
moduleBaseEval(
""
#ifdef DUSK_INPUT_KEYBOARD
"var INPUT_KEYBOARD = true;\n"
#endif
#ifdef DUSK_INPUT_GAMEPAD
"var INPUT_GAMEPAD = true;\n"
#endif
#ifdef DUSK_INPUT_POINTER
"var INPUT_POINTER = true;\n"
#endif
#ifdef DUSK_INPUT_TOUCH
"var INPUT_TOUCH = true;\n"
#endif
);
scriptProtoInit(
&MODULE_INPUT_PROTO, "Input", sizeof(uint8_t), NULL
);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "bind", moduleInputBind
);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "isDown", moduleInputIsDown
);
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, "axis2D", moduleInputAxis2D
);
}
@@ -1,9 +1,10 @@
# Copyright (c) 2026 Dominic Masters # Copyright (c) 2026 Dominic Masters
# #
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
timedolphin.c modulelocale.c
) )
@@ -0,0 +1,99 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "modulelocale.h"
#include "locale/localemanager.h"
#include "assert/assert.h"
void moduleLocale(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL");
// Execute the locale script definitions.
lua_register(context->luaState, "localeGetText", moduleLocaleGetText);
}
int moduleLocaleGetText(lua_State *L) {
// Expect string param for the id
if(!lua_isstring(L, 1)) {
luaL_error(L, "Expected message ID as first argument");
return 0;
}
const char_t *id = lua_tostring(L, 1);
if(id == NULL || strlen(id) == 0) {
luaL_error(L, "Message ID cannot be NULL or empty");
return 0;
}
// Optional plural param, default to 0
int32_t plural = 0;
int top = lua_gettop(L);
int argStart = 2;
if(top >= 2 && !lua_isnil(L, 2)) {
if(!lua_isnumber(L, 2)) {
luaL_error(L, "Expected plural as second argument");
return 0;
}
plural = (int32_t)lua_tointeger(L, 2);
if(plural < 0) {
luaL_error(L, "Plural cannot be negative");
return 0;
}
argStart = 3;
}
// Build structured arg list from remaining Lua args
size_t argCount = (top >= argStart) ? (size_t)(top - argStart + 1) : 0;
#define MODULE_LOCALE_MAX_ARGS 16
assetlocalearg_t argsStack[MODULE_LOCALE_MAX_ARGS];
assetlocalearg_t *args = argsStack;
if(argCount > MODULE_LOCALE_MAX_ARGS) {
luaL_error(L, "Too many args (max %d)", MODULE_LOCALE_MAX_ARGS);
return 0;
}
for(size_t i = 0; i < argCount; ++i) {
int luaIndex = argStart + (int)i;
if(lua_isinteger(L, luaIndex)) {
args[i].type = ASSET_LOCALE_ARG_INT;
args[i].intValue = (int32_t)lua_tonumber(L, luaIndex);
} else if(lua_isnumber(L, luaIndex)) {
args[i].type = ASSET_LOCALE_ARG_FLOAT;
args[i].floatValue = lua_tonumber(L, luaIndex);
} else if(lua_isstring(L, luaIndex)) {
args[i].type = ASSET_LOCALE_ARG_STRING;
args[i].stringValue = lua_tostring(L, luaIndex);
} else {
luaL_error(L, "Unsupported localization argument type");
return 0;
}
}
char_t buffer[1024];
errorret_t err = localeManagerGetTextArgs(
id,
buffer,
sizeof(buffer),
plural,
args,
argCount
);
if(err.code != ERROR_OK) {
errorCatch(errorPrint(err));
luaL_error(L, "Failed to get localized text for ID '%s'", id);
return 0;
}
lua_pushstring(L, buffer);
return 1;
}
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Register locale functions to the given script context.
*
* @param context The script context to register locale functions to.
*/
void moduleLocale(scriptcontext_t *context);
/**
* Script binding for getting a localized string.
*
* @param L The Lua state.
* @return The number of return values on the Lua stack.
*/
int moduleLocaleGetText(lua_State *L);
-229
View File
@@ -1,229 +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 "cglm/cglm.h"
#include "modulevec3.h"
#include "modulevec4.h"
static scriptproto_t MODULE_MAT4_PROTO;
static inline void * moduleMatGet(const jerry_call_info_t *callInfo) {
return scriptProtoGetValue(&MODULE_MAT4_PROTO, callInfo->this_value);
}
moduleBaseFunction(moduleMatConstructor) {
float_t (*ptr)[4] = (float_t (*)[4])memoryAllocate(sizeof(mat4));
glm_mat4_identity(ptr);
jerry_object_set_native_ptr(
callInfo->this_value, &MODULE_MAT4_PROTO.info, ptr
);
return jerry_undefined();
}
moduleBaseFunction(moduleMatMul) {
moduleBaseRequireArgs(1);
float_t (*a)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!a) return moduleBaseThrow("Mat4.mul: invalid this");
float_t (*b)[4] = (float_t (*)[4])scriptProtoGetValue(
&MODULE_MAT4_PROTO, args[0]
);
if(!b) return moduleBaseThrow("Mat4.mul: argument must be a Mat4");
mat4 r;
glm_mat4_mul(a, b, r);
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
}
moduleBaseFunction(moduleMatTranspose) {
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!m) return moduleBaseThrow("Mat4.transpose: invalid this");
mat4 r;
glm_mat4_transpose_to(m, r);
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
}
moduleBaseFunction(moduleMatInverse) {
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!m) return moduleBaseThrow("Mat4.inverse: invalid this");
mat4 r;
glm_mat4_inv(m, r);
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
}
moduleBaseFunction(moduleMatDeterminant) {
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!m) return moduleBaseThrow("Mat4.determinant: invalid this");
return jerry_number(glm_mat4_det(m));
}
moduleBaseFunction(moduleMatMulVec3) {
moduleBaseRequireArgs(1);
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!m) return moduleBaseThrow("Mat4.mulVec3: invalid this");
vec3 vin;
if(!moduleVec3Check(args[0], vin)) {
return moduleBaseThrow("Mat4.mulVec3: argument must be a Vec3");
}
float_t w = (argc >= 2 && jerry_value_is_number(args[1]))
? (float_t)jerry_value_as_number(args[1]) : 1.0f;
vec3 vout;
glm_mat4_mulv3(m, vin, w, vout);
return moduleVec3Push(vout);
}
moduleBaseFunction(moduleMatMulVec4) {
moduleBaseRequireArgs(1);
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!m) return moduleBaseThrow("Mat4.mulVec4: invalid this");
vec4 vin;
if(!moduleVec4Check(args[0], vin)) {
return moduleBaseThrow("Mat4.mulVec4: argument must be a Vec4");
}
vec4 vout;
glm_mat4_mulv(m, vin, vout);
return moduleVec4Push(vout);
}
moduleBaseFunction(moduleMatTranslate) {
moduleBaseRequireArgs(1);
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!m) return moduleBaseThrow("Mat4.translate: invalid this");
vec3 tv;
if(!moduleVec3Check(args[0], tv)) {
return moduleBaseThrow("Mat4.translate: argument must be a Vec3");
}
mat4 r;
glm_mat4_copy(m, r);
glm_translate(r, tv);
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
}
moduleBaseFunction(moduleMatScale) {
moduleBaseRequireArgs(1);
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!m) return moduleBaseThrow("Mat4.scale: invalid this");
vec3 sv;
if(!moduleVec3Check(args[0], sv)) {
return moduleBaseThrow("Mat4.scale: argument must be a Vec3");
}
mat4 r;
glm_mat4_copy(m, r);
glm_scale(r, sv);
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
}
moduleBaseFunction(moduleMatStaticIdentity) {
mat4 r;
glm_mat4_identity(r);
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
}
moduleBaseFunction(moduleMatStaticPerspective) {
moduleBaseRequireArgs(4);
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1);
moduleBaseRequireNumber(2); moduleBaseRequireNumber(3);
mat4 r;
glm_perspective(
(float_t)jerry_value_as_number(args[0]),
(float_t)jerry_value_as_number(args[1]),
(float_t)jerry_value_as_number(args[2]),
(float_t)jerry_value_as_number(args[3]),
r
);
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
}
moduleBaseFunction(moduleMatStaticLookAt) {
moduleBaseRequireArgs(3);
vec3 eye, center, up;
if(!moduleVec3Check(args[0], eye)) {
return moduleBaseThrow("Mat4.lookAt: eye must be a Vec3");
}
if(!moduleVec3Check(args[1], center)) {
return moduleBaseThrow("Mat4.lookAt: center must be a Vec3");
}
if(!moduleVec3Check(args[2], up)) {
return moduleBaseThrow("Mat4.lookAt: up must be a Vec3");
}
mat4 r;
glm_lookat(eye, center, up, r);
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
}
moduleBaseFunction(moduleMatToString) {
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
if(!m) return jerry_string_sz("Mat4(?)");
char_t buf[256];
stringFormat(
buf, sizeof(buf),
"Mat4([%g,%g,%g,%g], [%g,%g,%g,%g],"
" [%g,%g,%g,%g], [%g,%g,%g,%g])",
m[0][0], m[0][1], m[0][2], m[0][3],
m[1][0], m[1][1], m[1][2], m[1][3],
m[2][0], m[2][1], m[2][2], m[2][3],
m[3][0], m[3][1], m[3][2], m[3][3]
);
return jerry_string_sz(buf);
}
static inline jerry_value_t moduleMat4Push(float (*m)[4]) {
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, m);
}
static inline bool_t moduleMat4Check(jerry_value_t val, float (*out)[4]) {
float_t (*m)[4] = (float_t (*)[4])scriptProtoGetValue(
&MODULE_MAT4_PROTO, val
);
if(!m) return false;
glm_mat4_copy(m, out);
return true;
}
static void moduleMat4(void) {
scriptProtoInit(
&MODULE_MAT4_PROTO, "Mat4", sizeof(mat4), moduleMatConstructor
);
scriptProtoDefineToString(&MODULE_MAT4_PROTO, moduleMatToString);
scriptProtoDefineFunc(
&MODULE_MAT4_PROTO, "mul", moduleMatMul
);
scriptProtoDefineFunc(
&MODULE_MAT4_PROTO, "transpose", moduleMatTranspose
);
scriptProtoDefineFunc(
&MODULE_MAT4_PROTO, "inverse", moduleMatInverse
);
scriptProtoDefineFunc(
&MODULE_MAT4_PROTO, "determinant", moduleMatDeterminant
);
scriptProtoDefineFunc(
&MODULE_MAT4_PROTO, "mulVec3", moduleMatMulVec3
);
scriptProtoDefineFunc(
&MODULE_MAT4_PROTO, "mulVec4", moduleMatMulVec4
);
scriptProtoDefineFunc(
&MODULE_MAT4_PROTO, "translate", moduleMatTranslate
);
scriptProtoDefineFunc(
&MODULE_MAT4_PROTO, "scale", moduleMatScale
);
scriptProtoDefineStaticFunc(
&MODULE_MAT4_PROTO, "identity", moduleMatStaticIdentity
);
scriptProtoDefineStaticFunc(
&MODULE_MAT4_PROTO, "perspective", moduleMatStaticPerspective
);
scriptProtoDefineStaticFunc(
&MODULE_MAT4_PROTO, "lookAt", moduleMatStaticLookAt
);
}
-22
View File
@@ -1,22 +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 "modulevec2.h"
#include "modulevec3.h"
#include "modulevec3ref.h"
#include "modulevec4.h"
#include "modulemat4.h"
static void moduleMath(void) {
moduleVec2();
moduleVec3();
moduleVec3Ref();
moduleVec4();
moduleMat4();
}
-189
View File
@@ -1,189 +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 "cglm/cglm.h"
static scriptproto_t MODULE_VEC2_PROTO;
static inline float_t * moduleVec2Get(const jerry_call_info_t *callInfo) {
return (float_t *)scriptProtoGetValue(
&MODULE_VEC2_PROTO, callInfo->this_value
);
}
static inline float_t * moduleVec2From(jerry_value_t val) {
return (float_t *)scriptProtoGetValue(&MODULE_VEC2_PROTO, val);
}
moduleBaseFunction(moduleVec2Constructor) {
float_t *ptr = (float_t *)memoryAllocate(sizeof(vec2));
ptr[0] = (argc >= 1 && jerry_value_is_number(args[0]))
? (float_t)jerry_value_as_number(args[0]) : 0.0f;
ptr[1] = (argc >= 2 && jerry_value_is_number(args[1]))
? (float_t)jerry_value_as_number(args[1]) : 0.0f;
jerry_object_set_native_ptr(
callInfo->this_value, &MODULE_VEC2_PROTO.info, ptr
);
return jerry_undefined();
}
moduleBaseFunction(moduleVec2GetX) {
float_t *v = moduleVec2Get(callInfo);
return v ? jerry_number(v[0]) : jerry_undefined();
}
moduleBaseFunction(moduleVec2SetX) {
float_t *v = moduleVec2Get(callInfo);
if(v && argc > 0) v[0] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec2GetY) {
float_t *v = moduleVec2Get(callInfo);
return v ? jerry_number(v[1]) : jerry_undefined();
}
moduleBaseFunction(moduleVec2SetY) {
float_t *v = moduleVec2Get(callInfo);
if(v && argc > 0) v[1] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec2Dot) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec2Get(callInfo);
if(!a) return moduleBaseThrow("Vec2.dot: invalid this");
float_t *b = moduleVec2From(args[0]);
if(!b) return moduleBaseThrow("Vec2.dot: argument must be a Vec2");
return jerry_number(glm_vec2_dot(a, b));
}
moduleBaseFunction(moduleVec2Length) {
float_t *v = moduleVec2Get(callInfo);
if(!v) return moduleBaseThrow("Vec2.length: invalid this");
return jerry_number(glm_vec2_norm(v));
}
moduleBaseFunction(moduleVec2LengthSq) {
float_t *v = moduleVec2Get(callInfo);
if(!v) return moduleBaseThrow("Vec2.lengthSq: invalid this");
return jerry_number(glm_vec2_norm2(v));
}
moduleBaseFunction(moduleVec2Normalize) {
float_t *v = moduleVec2Get(callInfo);
if(!v) return moduleBaseThrow("Vec2.normalize: invalid this");
vec2 r;
glm_vec2_normalize_to(v, r);
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
}
moduleBaseFunction(moduleVec2Negate) {
float_t *v = moduleVec2Get(callInfo);
if(!v) return moduleBaseThrow("Vec2.negate: invalid this");
vec2 r;
glm_vec2_negate_to(v, r);
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
}
moduleBaseFunction(moduleVec2Add) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec2Get(callInfo);
if(!a) return moduleBaseThrow("Vec2.add: invalid this");
float_t *b = moduleVec2From(args[0]);
if(!b) return moduleBaseThrow("Vec2.add: argument must be a Vec2");
vec2 r;
glm_vec2_add(a, b, r);
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
}
moduleBaseFunction(moduleVec2Sub) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec2Get(callInfo);
if(!a) return moduleBaseThrow("Vec2.sub: invalid this");
float_t *b = moduleVec2From(args[0]);
if(!b) return moduleBaseThrow("Vec2.sub: argument must be a Vec2");
vec2 r;
glm_vec2_sub(a, b, r);
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
}
moduleBaseFunction(moduleVec2Scale) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
float_t *v = moduleVec2Get(callInfo);
if(!v) return moduleBaseThrow("Vec2.scale: invalid this");
vec2 r;
glm_vec2_scale(v, (float_t)jerry_value_as_number(args[0]), r);
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
}
moduleBaseFunction(moduleVec2Lerp) {
moduleBaseRequireArgs(2); moduleBaseRequireNumber(1);
float_t *a = moduleVec2Get(callInfo);
if(!a) return moduleBaseThrow("Vec2.lerp: invalid this");
float_t *b = moduleVec2From(args[0]);
if(!b) return moduleBaseThrow("Vec2.lerp: first argument must be a Vec2");
vec2 r;
glm_vec2_lerp(a, b, (float_t)jerry_value_as_number(args[1]), r);
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
}
moduleBaseFunction(moduleVec2Distance) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec2Get(callInfo);
if(!a) return moduleBaseThrow("Vec2.distance: invalid this");
float_t *b = moduleVec2From(args[0]);
if(!b) return moduleBaseThrow("Vec2.distance: argument must be a Vec2");
return jerry_number(glm_vec2_distance(a, b));
}
moduleBaseFunction(moduleVec2ToString) {
float_t *v = moduleVec2Get(callInfo);
if(!v) return jerry_string_sz("Vec2(?, ?)");
char_t buf[64];
stringFormat(buf, sizeof(buf), "Vec2(%g, %g)", v[0], v[1]);
return jerry_string_sz(buf);
}
static inline jerry_value_t moduleVec2Push(const float_t *v) {
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, v);
}
static inline bool_t moduleVec2Check(jerry_value_t val, float_t *out) {
float_t *v = moduleVec2From(val);
if(!v) return false;
out[0] = v[0];
out[1] = v[1];
return true;
}
static void moduleVec2(void) {
scriptProtoInit(
&MODULE_VEC2_PROTO, "Vec2", sizeof(vec2), moduleVec2Constructor
);
scriptProtoDefineProp(
&MODULE_VEC2_PROTO, "x", moduleVec2GetX, moduleVec2SetX
);
scriptProtoDefineProp(
&MODULE_VEC2_PROTO, "y", moduleVec2GetY, moduleVec2SetY
);
scriptProtoDefineToString(&MODULE_VEC2_PROTO, moduleVec2ToString);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "dot", moduleVec2Dot);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "length", moduleVec2Length);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "lengthSq", moduleVec2LengthSq);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "normalize", moduleVec2Normalize);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "negate", moduleVec2Negate);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "add", moduleVec2Add);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "sub", moduleVec2Sub);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "scale", moduleVec2Scale);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "lerp", moduleVec2Lerp);
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "distance", moduleVec2Distance);
}
-217
View File
@@ -1,217 +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 "cglm/cglm.h"
static scriptproto_t MODULE_VEC3_PROTO;
static inline float_t * moduleVec3Get(const jerry_call_info_t *callInfo) {
return (float_t *)scriptProtoGetValue(
&MODULE_VEC3_PROTO, callInfo->this_value
);
}
static inline float_t * moduleVec3From(jerry_value_t val) {
return (float_t *)scriptProtoGetValue(&MODULE_VEC3_PROTO, val);
}
moduleBaseFunction(moduleVec3Constructor) {
float_t *ptr = (float_t *)memoryAllocate(sizeof(vec3));
ptr[0] = (argc >= 1 && jerry_value_is_number(args[0]))
? (float_t)jerry_value_as_number(args[0]) : 0.0f;
ptr[1] = (argc >= 2 && jerry_value_is_number(args[1]))
? (float_t)jerry_value_as_number(args[1]) : 0.0f;
ptr[2] = (argc >= 3 && jerry_value_is_number(args[2]))
? (float_t)jerry_value_as_number(args[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);
return v ? jerry_number(v[0]) : jerry_undefined();
}
moduleBaseFunction(moduleVec3SetX) {
float_t *v = moduleVec3Get(callInfo);
if(v && argc > 0) v[0] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec3GetY) {
float_t *v = moduleVec3Get(callInfo);
return v ? jerry_number(v[1]) : jerry_undefined();
}
moduleBaseFunction(moduleVec3SetY) {
float_t *v = moduleVec3Get(callInfo);
if(v && argc > 0) v[1] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec3GetZ) {
float_t *v = moduleVec3Get(callInfo);
return v ? jerry_number(v[2]) : jerry_undefined();
}
moduleBaseFunction(moduleVec3SetZ) {
float_t *v = moduleVec3Get(callInfo);
if(v && argc > 0) v[2] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec3Dot) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec3Get(callInfo);
if(!a) return moduleBaseThrow("Vec3.dot: invalid this");
float_t *b = moduleVec3From(args[0]);
if(!b) return moduleBaseThrow("Vec3.dot: argument must be a Vec3");
return jerry_number(glm_vec3_dot(a, b));
}
moduleBaseFunction(moduleVec3Cross) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec3Get(callInfo);
if(!a) return moduleBaseThrow("Vec3.cross: invalid this");
float_t *b = moduleVec3From(args[0]);
if(!b) return moduleBaseThrow("Vec3.cross: argument must be a Vec3");
vec3 r;
glm_vec3_cross(a, b, r);
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
}
moduleBaseFunction(moduleVec3Length) {
float_t *v = moduleVec3Get(callInfo);
if(!v) return moduleBaseThrow("Vec3.length: invalid this");
return jerry_number(glm_vec3_norm(v));
}
moduleBaseFunction(moduleVec3LengthSq) {
float_t *v = moduleVec3Get(callInfo);
if(!v) return moduleBaseThrow("Vec3.lengthSq: invalid this");
return jerry_number(glm_vec3_norm2(v));
}
moduleBaseFunction(moduleVec3Normalize) {
float_t *v = moduleVec3Get(callInfo);
if(!v) return moduleBaseThrow("Vec3.normalize: invalid this");
vec3 r;
glm_vec3_normalize_to(v, r);
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
}
moduleBaseFunction(moduleVec3Negate) {
float_t *v = moduleVec3Get(callInfo);
if(!v) return moduleBaseThrow("Vec3.negate: invalid this");
vec3 r;
glm_vec3_negate_to(v, r);
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
}
moduleBaseFunction(moduleVec3Add) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec3Get(callInfo);
if(!a) return moduleBaseThrow("Vec3.add: invalid this");
float_t *b = moduleVec3From(args[0]);
if(!b) return moduleBaseThrow("Vec3.add: argument must be a Vec3");
vec3 r;
glm_vec3_add(a, b, r);
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
}
moduleBaseFunction(moduleVec3Sub) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec3Get(callInfo);
if(!a) return moduleBaseThrow("Vec3.sub: invalid this");
float_t *b = moduleVec3From(args[0]);
if(!b) return moduleBaseThrow("Vec3.sub: argument must be a Vec3");
vec3 r;
glm_vec3_sub(a, b, r);
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
}
moduleBaseFunction(moduleVec3Scale) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
float_t *v = moduleVec3Get(callInfo);
if(!v) return moduleBaseThrow("Vec3.scale: invalid this");
vec3 r;
glm_vec3_scale(v, (float_t)jerry_value_as_number(args[0]), r);
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
}
moduleBaseFunction(moduleVec3Lerp) {
moduleBaseRequireArgs(2); moduleBaseRequireNumber(1);
float_t *a = moduleVec3Get(callInfo);
if(!a) return moduleBaseThrow("Vec3.lerp: invalid this");
float_t *b = moduleVec3From(args[0]);
if(!b) return moduleBaseThrow("Vec3.lerp: first argument must be a Vec3");
vec3 r;
glm_vec3_lerp(a, b, (float_t)jerry_value_as_number(args[1]), r);
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
}
moduleBaseFunction(moduleVec3Distance) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec3Get(callInfo);
if(!a) return moduleBaseThrow("Vec3.distance: invalid this");
float_t *b = moduleVec3From(args[0]);
if(!b) return moduleBaseThrow("Vec3.distance: argument must be a Vec3");
return jerry_number(glm_vec3_distance(a, b));
}
moduleBaseFunction(moduleVec3ToString) {
float_t *v = moduleVec3Get(callInfo);
if(!v) return jerry_string_sz("Vec3(?, ?, ?)");
char_t buf[80];
stringFormat(buf, sizeof(buf), "Vec3(%g, %g, %g)", v[0], v[1], v[2]);
return jerry_string_sz(buf);
}
static inline jerry_value_t moduleVec3Push(const float_t *v) {
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, v);
}
static inline bool_t moduleVec3Check(jerry_value_t val, float_t *out) {
float_t *v = moduleVec3From(val);
if(!v) return false;
out[0] = v[0];
out[1] = v[1];
out[2] = v[2];
return true;
}
static void moduleVec3(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);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "dot", moduleVec3Dot);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "cross", moduleVec3Cross);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "length", moduleVec3Length);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "lengthSq", moduleVec3LengthSq);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "normalize", moduleVec3Normalize);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "negate", moduleVec3Negate);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "add", moduleVec3Add);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "sub", moduleVec3Sub);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "scale", moduleVec3Scale);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "lerp", moduleVec3Lerp);
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "distance", moduleVec3Distance);
}
-126
View File
@@ -1,126 +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 "cglm/cglm.h"
#include "modulevec3.h"
typedef struct {
float_t *data;
void (*onChange)(void *ctx);
void *ctx;
} vec3ref_t;
static scriptproto_t MODULE_VEC3_REF_PROTO;
static inline vec3ref_t * moduleVec3RefGet(
const jerry_call_info_t *callInfo
) {
return (vec3ref_t*)scriptProtoGetValue(
&MODULE_VEC3_REF_PROTO, callInfo->this_value
);
}
static inline vec3ref_t * moduleVec3RefFrom(jerry_value_t val) {
return (vec3ref_t*)scriptProtoGetValue(&MODULE_VEC3_REF_PROTO, val);
}
static inline void moduleVec3RefNotify(vec3ref_t *ref) {
if(ref->onChange) ref->onChange(ref->ctx);
}
moduleBaseFunction(moduleVec3RefGetX) {
vec3ref_t *ref = moduleVec3RefGet(callInfo);
return ref ? jerry_number(ref->data[0]) : jerry_undefined();
}
moduleBaseFunction(moduleVec3RefSetX) {
vec3ref_t *ref = moduleVec3RefGet(callInfo);
if(ref && argc > 0) {
ref->data[0] = (float_t)jerry_value_as_number(args[0]);
moduleVec3RefNotify(ref);
}
return jerry_undefined();
}
moduleBaseFunction(moduleVec3RefGetY) {
vec3ref_t *ref = moduleVec3RefGet(callInfo);
return ref ? jerry_number(ref->data[1]) : jerry_undefined();
}
moduleBaseFunction(moduleVec3RefSetY) {
vec3ref_t *ref = moduleVec3RefGet(callInfo);
if(ref && argc > 0) {
ref->data[1] = (float_t)jerry_value_as_number(args[0]);
moduleVec3RefNotify(ref);
}
return jerry_undefined();
}
moduleBaseFunction(moduleVec3RefGetZ) {
vec3ref_t *ref = moduleVec3RefGet(callInfo);
return ref ? jerry_number(ref->data[2]) : jerry_undefined();
}
moduleBaseFunction(moduleVec3RefSetZ) {
vec3ref_t *ref = moduleVec3RefGet(callInfo);
if(ref && argc > 0) {
ref->data[2] = (float_t)jerry_value_as_number(args[0]);
moduleVec3RefNotify(ref);
}
return jerry_undefined();
}
moduleBaseFunction(moduleVec3RefToString) {
vec3ref_t *ref = moduleVec3RefGet(callInfo);
if(!ref) return jerry_string_sz("Vec3(?, ?, ?)");
char_t buf[80];
stringFormat(
buf, sizeof(buf),
"Vec3(%g, %g, %g)", ref->data[0], ref->data[1], ref->data[2]
);
return jerry_string_sz(buf);
}
static inline jerry_value_t moduleVec3RefPush(
float_t *data, void (*onChange)(void *), void *ctx
) {
vec3ref_t ref = { .data = data, .onChange = onChange, .ctx = ctx };
return scriptProtoCreateValue(&MODULE_VEC3_REF_PROTO, &ref);
}
static inline bool_t moduleVec3RefCheck(jerry_value_t val, float_t *out) {
vec3ref_t *ref = moduleVec3RefFrom(val);
if(!ref) return false;
out[0] = ref->data[0];
out[1] = ref->data[1];
out[2] = ref->data[2];
return true;
}
static inline bool_t moduleVec3AnyCheck(jerry_value_t val, float_t *out) {
return moduleVec3Check(val, out) || moduleVec3RefCheck(val, out);
}
static void moduleVec3Ref(void) {
scriptProtoInit(
&MODULE_VEC3_REF_PROTO, NULL, sizeof(vec3ref_t), NULL
);
scriptProtoDefineToString(&MODULE_VEC3_REF_PROTO, moduleVec3RefToString);
scriptProtoDefineProp(
&MODULE_VEC3_REF_PROTO, "x",
moduleVec3RefGetX, moduleVec3RefSetX
);
scriptProtoDefineProp(
&MODULE_VEC3_REF_PROTO, "y",
moduleVec3RefGetY, moduleVec3RefSetY
);
scriptProtoDefineProp(
&MODULE_VEC3_REF_PROTO, "z",
moduleVec3RefGetZ, moduleVec3RefSetZ
);
}
-268
View File
@@ -1,268 +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 "cglm/cglm.h"
static scriptproto_t MODULE_VEC4_PROTO;
static inline float_t * moduleVec4Get(const jerry_call_info_t *callInfo) {
return (float_t *)scriptProtoGetValue(
&MODULE_VEC4_PROTO, callInfo->this_value
);
}
static inline float_t * moduleVec4From(jerry_value_t val) {
return (float_t *)scriptProtoGetValue(&MODULE_VEC4_PROTO, val);
}
moduleBaseFunction(moduleVec4Constructor) {
float_t *ptr = (float_t *)memoryAllocate(sizeof(vec4));
ptr[0] = (argc >= 1 && jerry_value_is_number(args[0]))
? (float_t)jerry_value_as_number(args[0]) : 0.0f;
ptr[1] = (argc >= 2 && jerry_value_is_number(args[1]))
? (float_t)jerry_value_as_number(args[1]) : 0.0f;
ptr[2] = (argc >= 3 && jerry_value_is_number(args[2]))
? (float_t)jerry_value_as_number(args[2]) : 0.0f;
ptr[3] = (argc >= 4 && jerry_value_is_number(args[3]))
? (float_t)jerry_value_as_number(args[3]) : 0.0f;
jerry_object_set_native_ptr(
callInfo->this_value, &MODULE_VEC4_PROTO.info, ptr
);
return jerry_undefined();
}
// x/y/z/w
moduleBaseFunction(moduleVec4GetX) {
float_t *v = moduleVec4Get(callInfo);
return v ? jerry_number(v[0]) : jerry_undefined();
}
moduleBaseFunction(moduleVec4SetX) {
float_t *v = moduleVec4Get(callInfo);
if(v && argc > 0) v[0] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec4GetY) {
float_t *v = moduleVec4Get(callInfo);
return v ? jerry_number(v[1]) : jerry_undefined();
}
moduleBaseFunction(moduleVec4SetY) {
float_t *v = moduleVec4Get(callInfo);
if(v && argc > 0) v[1] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec4GetZ) {
float_t *v = moduleVec4Get(callInfo);
return v ? jerry_number(v[2]) : jerry_undefined();
}
moduleBaseFunction(moduleVec4SetZ) {
float_t *v = moduleVec4Get(callInfo);
if(v && argc > 0) v[2] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec4GetW) {
float_t *v = moduleVec4Get(callInfo);
return v ? jerry_number(v[3]) : jerry_undefined();
}
moduleBaseFunction(moduleVec4SetW) {
float_t *v = moduleVec4Get(callInfo);
if(v && argc > 0) v[3] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
// u0/v0/u1/v1 aliases for UV coordinates
moduleBaseFunction(moduleVec4GetU0) {
float_t *v = moduleVec4Get(callInfo);
return v ? jerry_number(v[0]) : jerry_undefined();
}
moduleBaseFunction(moduleVec4SetU0) {
float_t *v = moduleVec4Get(callInfo);
if(v && argc > 0) v[0] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec4GetV0) {
float_t *v = moduleVec4Get(callInfo);
return v ? jerry_number(v[1]) : jerry_undefined();
}
moduleBaseFunction(moduleVec4SetV0) {
float_t *v = moduleVec4Get(callInfo);
if(v && argc > 0) v[1] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec4GetU1) {
float_t *v = moduleVec4Get(callInfo);
return v ? jerry_number(v[2]) : jerry_undefined();
}
moduleBaseFunction(moduleVec4SetU1) {
float_t *v = moduleVec4Get(callInfo);
if(v && argc > 0) v[2] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec4GetV1) {
float_t *v = moduleVec4Get(callInfo);
return v ? jerry_number(v[3]) : jerry_undefined();
}
moduleBaseFunction(moduleVec4SetV1) {
float_t *v = moduleVec4Get(callInfo);
if(v && argc > 0) v[3] = (float_t)jerry_value_as_number(args[0]);
return jerry_undefined();
}
moduleBaseFunction(moduleVec4Dot) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec4Get(callInfo);
if(!a) return moduleBaseThrow("Vec4.dot: invalid this");
float_t *b = moduleVec4From(args[0]);
if(!b) return moduleBaseThrow("Vec4.dot: argument must be a Vec4");
return jerry_number(glm_vec4_dot(a, b));
}
moduleBaseFunction(moduleVec4Length) {
float_t *v = moduleVec4Get(callInfo);
if(!v) return moduleBaseThrow("Vec4.length: invalid this");
return jerry_number(glm_vec4_norm(v));
}
moduleBaseFunction(moduleVec4LengthSq) {
float_t *v = moduleVec4Get(callInfo);
if(!v) return moduleBaseThrow("Vec4.lengthSq: invalid this");
return jerry_number(glm_vec4_norm2(v));
}
moduleBaseFunction(moduleVec4Normalize) {
float_t *v = moduleVec4Get(callInfo);
if(!v) return moduleBaseThrow("Vec4.normalize: invalid this");
vec4 r;
glm_vec4_normalize_to(v, r);
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
}
moduleBaseFunction(moduleVec4Negate) {
float_t *v = moduleVec4Get(callInfo);
if(!v) return moduleBaseThrow("Vec4.negate: invalid this");
vec4 r;
glm_vec4_negate_to(v, r);
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
}
moduleBaseFunction(moduleVec4Add) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec4Get(callInfo);
if(!a) return moduleBaseThrow("Vec4.add: invalid this");
float_t *b = moduleVec4From(args[0]);
if(!b) return moduleBaseThrow("Vec4.add: argument must be a Vec4");
vec4 r;
glm_vec4_add(a, b, r);
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
}
moduleBaseFunction(moduleVec4Sub) {
moduleBaseRequireArgs(1);
float_t *a = moduleVec4Get(callInfo);
if(!a) return moduleBaseThrow("Vec4.sub: invalid this");
float_t *b = moduleVec4From(args[0]);
if(!b) return moduleBaseThrow("Vec4.sub: argument must be a Vec4");
vec4 r;
glm_vec4_sub(a, b, r);
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
}
moduleBaseFunction(moduleVec4Scale) {
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
float_t *v = moduleVec4Get(callInfo);
if(!v) return moduleBaseThrow("Vec4.scale: invalid this");
vec4 r;
glm_vec4_scale(v, (float_t)jerry_value_as_number(args[0]), r);
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
}
moduleBaseFunction(moduleVec4Lerp) {
moduleBaseRequireArgs(2); moduleBaseRequireNumber(1);
float_t *a = moduleVec4Get(callInfo);
if(!a) return moduleBaseThrow("Vec4.lerp: invalid this");
float_t *b = moduleVec4From(args[0]);
if(!b) return moduleBaseThrow("Vec4.lerp: first argument must be a Vec4");
vec4 r;
glm_vec4_lerp(a, b, (float_t)jerry_value_as_number(args[1]), r);
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
}
moduleBaseFunction(moduleVec4ToString) {
float_t *v = moduleVec4Get(callInfo);
if(!v) return jerry_string_sz("Vec4(?, ?, ?, ?)");
char_t buf[96];
stringFormat(
buf, sizeof(buf),
"Vec4(%g, %g, %g, %g)", v[0], v[1], v[2], v[3]
);
return jerry_string_sz(buf);
}
static inline jerry_value_t moduleVec4Push(const float_t *v) {
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, v);
}
static inline bool_t moduleVec4Check(jerry_value_t val, float_t *out) {
float_t *v = moduleVec4From(val);
if(!v) return false;
out[0] = v[0];
out[1] = v[1];
out[2] = v[2];
out[3] = v[3];
return true;
}
static void moduleVec4(void) {
scriptProtoInit(
&MODULE_VEC4_PROTO, "Vec4", sizeof(vec4), moduleVec4Constructor
);
scriptProtoDefineProp(
&MODULE_VEC4_PROTO, "x", moduleVec4GetX, moduleVec4SetX
);
scriptProtoDefineProp(
&MODULE_VEC4_PROTO, "y", moduleVec4GetY, moduleVec4SetY
);
scriptProtoDefineProp(
&MODULE_VEC4_PROTO, "z", moduleVec4GetZ, moduleVec4SetZ
);
scriptProtoDefineProp(
&MODULE_VEC4_PROTO, "w", moduleVec4GetW, moduleVec4SetW
);
scriptProtoDefineProp(
&MODULE_VEC4_PROTO, "u0", moduleVec4GetU0, moduleVec4SetU0
);
scriptProtoDefineProp(
&MODULE_VEC4_PROTO, "v0", moduleVec4GetV0, moduleVec4SetV0
);
scriptProtoDefineProp(
&MODULE_VEC4_PROTO, "u1", moduleVec4GetU1, moduleVec4SetU1
);
scriptProtoDefineProp(
&MODULE_VEC4_PROTO, "v1", moduleVec4GetV1, moduleVec4SetV1
);
scriptProtoDefineToString(&MODULE_VEC4_PROTO, moduleVec4ToString);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "dot", moduleVec4Dot);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "length", moduleVec4Length);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "lengthSq", moduleVec4LengthSq);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "normalize", moduleVec4Normalize);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "negate", moduleVec4Negate);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "add", moduleVec4Add);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "sub", moduleVec4Sub);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "scale", moduleVec4Scale);
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "lerp", moduleVec4Lerp);
}
-33
View File
@@ -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/script/moduleinclude.h"
#include "script/module/math/modulemath.h"
#include "script/module/entity/moduleentity.h"
#include "script/module/input/moduleinput.h"
#include "script/module/moduleplatform.h"
#include "script/module/time/moduletime.h"
#include "script/module/display/modulecolor.h"
#include "script/module/display/modulescreen.h"
#include "script/module/scene/modulescene.h"
#include "script/module/console/moduleconsole.h"
#include "script/module/engine/moduleengine.h"
static void moduleRegister(void) {
moduleInclude();
moduleMath();
moduleEntity();
moduleInput();
modulePlatform();
moduleTime();
moduleColor();
moduleScreen();
moduleScene();
moduleConsole();
moduleEngine();
}
-425
View File
@@ -1,425 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptmanager.h"
#include "assert/assert.h"
#include "util/string.h"
#include "util/memory.h"
#include <stdlib.h>
/**
* Define a function for a module in JavaScript.
*
* @param name Name of the method (in C, not JS)
* @return A C function with that name, containing the standard JS sig.
*/
#define moduleBaseFunction(name) \
static jerry_value_t name( \
const jerry_call_info_t *callInfo, \
const jerry_value_t args[], \
const jerry_length_t argc)
/**
* Define a standard JS prototype for a module.
* This creates the INFO struct, and a prototype reference value.
*
* Values are;
* {NAME}_INFO and {NAME_PROTOTYPE}
*
* @param name Name of the module.
* @return See above.
*/
#define moduleBaseProtoDefine(name) \
static const jerry_object_native_info_t name##_INFO = { \
.free_cb = moduleBaseFreeProto, \
.number_of_references = 0, \
.offset_of_references = 0 \
}; \
static jerry_value_t name##_PROTOTYPE = 0;
/**
* Gets the pointer to what is otherwise a prototype in the JerryScript code.
* This, for example, allows you to define a pointer in C and have it be a
* prototype for objects in JavaScript.
*
* @param object The JavaScript object to get the native pointer from.
* @param info The native info "prototype" struct used to get the pointer.
* @return The pointer to the proto (or NULL).
*/
static void* moduleBaseGetProto(
const jerry_value_t object,
const jerry_object_native_info_t *info
) {
assertNotNull(info, "Native info must not be null");
if(!jerry_value_is_object(object)) return NULL;
return (void*)jerry_object_get_native_ptr(object, info);
}
/**
* Create an object based on a C value.
*
* @param input The pointer to the value to create a JS object for.
* @param size The size of the data provided in input.
* @param info The native info "prototype" struct used to create the object.
* @return A JS object wrapping the provided C value.
*/
static jerry_value_t moduleBaseCreateProto(
void *input,
const size_t size,
const jerry_object_native_info_t *info,
const jerry_value_t prototype
) {
assertNotNull(input, "Input pointer must not be null");
assertTrue(size > 0, "Struct size must be greater than 0");
assertNotNull(info, "Native info must not be null");
void *ptr = memoryAllocate(size);
memoryCopy(ptr, input, size);
jerry_value_t proto = jerry_object();
jerry_object_set_native_ptr(proto, info, ptr);
jerry_object_set_proto(proto, prototype);
return proto;
}
/**
* Standard JerryScript free callback.
*
* @param ptr The pointer to free.
* @param info The native info struct associated with the pointer.
*/
static 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);
}
/**
* Quickly defines a property on a prototype with a getter and setter.
*/
static void moduleBaseProtoDefineProp(
const jerry_value_t prototype,
const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
) {
assertTrue(prototype != 0, "Prototype must not be null");
assertNotNull(name, "Property name must not be null");
assertNotNull(getter, "Getter must not be null");
jerry_property_descriptor_t desc;
memset(&desc, 0, 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(prototype, key, &desc);
jerry_value_free(result);
jerry_value_free(key);
jerry_value_free(desc.getter);
if(setter != NULL) jerry_value_free(desc.setter);
}
/**
* Register a global function for a module.
*
* @param name The name of the function as seen in JavaScript.
* @param fn The C handler function for the method.
*/
static void moduleBaseFunctionRegister(
const char_t *name,
jerry_external_handler_t fn
) {
assertNotNull(name, "Function name must not be null");
assertNotNull(fn, "Function handler must not be null");
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_value_t func = jerry_function_external(fn);
jerry_object_set(global, key, func);
jerry_value_free(func);
jerry_value_free(key);
jerry_value_free(global);
}
/**
* Evaluates a script in the global scope.
*
* @param script The script to evaluate.
*/
static void moduleBaseEval(const char_t *script) {
assertNotNull(script, "Script must not be null");
jerry_value_t result = jerry_eval(
(const jerry_char_t *)script,
strlen(script),
JERRY_PARSE_NO_OPTS
);
jerry_value_free(result);
}
/**
* Throw a type error from a module function.
*
* @param message The error message to throw.
* @return A JerryScript error value.
*/
static 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);
}
/**
* Set a global string constant.
*/
static void moduleBaseSetString(const char_t *name, const char_t *value) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_value_t val = jerry_string_sz(value);
jerry_object_set(global, key, val);
jerry_value_free(val);
jerry_value_free(key);
jerry_value_free(global);
}
/**
* Defines a global object with the provided name and prototype.
*
* @param name The name of the global object to create.
* @param prototype The prototype to set on the global object.
*/
static void moduleBaseCreateGlobalObject(
const char_t *name, jerry_value_t prototype
) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_object_set(global, key, prototype);
jerry_value_free(key);
jerry_value_free(global);
}
/**
* Assert at least n arguments were passed; return type error if not.
*/
#define moduleBaseRequireArgs(n) \
if((argc) < (n)) { \
return moduleBaseThrow("Expected at least " #n " arguments"); \
}
/**
* Assert an argument is a number; return type error if not.
*/
#define moduleBaseRequireNumber(i) do { \
if(!jerry_value_is_number(args[(i)])) { \
return moduleBaseThrow("Expected number argument"); \
} \
} while(0)
/**
* Assert an argument is a string; return type error if not.
*/
#define moduleBaseRequireString(i) do { \
if(!jerry_value_is_string(args[(i)])) { \
return moduleBaseThrow("Expected string argument"); \
} \
} while(0)
/**
* Assert an argument is a function; return type error if not.
*/
#define moduleBaseRequireFunction(i) do { \
if(!jerry_value_is_function(args[(i)])) { \
return moduleBaseThrow("Expected function argument"); \
} \
} while(0)
/**
* Assert an argument is an object; return type error if not.
*/
#define moduleBaseRequireObject(i) do { \
if(!jerry_value_is_object(args[(i)])) { \
return moduleBaseThrow("Expected object argument"); \
} \
} while(0)
/**
* Set a global numeric constant.
*/
static inline 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);
}
/**
* Set a global integer constant.
*/
static inline void moduleBaseSetInt(const char_t *name, int32_t value) {
moduleBaseSetNumber(name, (double)value);
}
/**
* Set a global JS value. Caller retains ownership of the value and must free
* it independently.
*/
static inline 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);
}
/**
* Wrap an engine-owned C pointer as a JS object (no GC free callback).
* Used for global singletons like INPUT_EVENT_PRESSED, SHADER_UNLIT, etc.
*/
static inline jerry_value_t moduleBaseWrapPointer(void *ptr) {
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &JS_PTR_NATIVE_INFO, ptr);
return obj;
}
/**
* Set a named global to a wrapped engine-owned C pointer.
* Combines moduleBaseWrapPointer and moduleBaseSetValue in one call.
*/
static inline void moduleBaseSetWrappedPointer(const char_t *name, void *ptr) {
jerry_value_t val = moduleBaseWrapPointer(ptr);
moduleBaseSetValue(name, val);
jerry_value_free(val);
}
/**
* Unwrap a C pointer from a JS object created by moduleBaseWrapPointer.
* Returns NULL if the object does not carry a matching native pointer.
*/
static inline void *moduleBaseUnwrapPointer(jerry_value_t val) {
if(!jerry_value_is_object(val)) return NULL;
return jerry_object_get_native_ptr(val, &JS_PTR_NATIVE_INFO);
}
/**
* Copy a JerryScript string value into a C buffer (null-terminated).
*
* @param val Jerry string value.
* @param buf Output buffer.
* @param buflen Buffer capacity including the null terminator.
*/
static inline 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';
}
/**
* Define a named property on a JS object with getter and optional setter.
*
* @param obj Target object (e.g. a prototype).
* @param name Property name.
* @param getter C getter handler.
* @param setter C setter handler, or NULL for read-only property.
*/
static inline void moduleBaseDefineProperty(
jerry_value_t obj,
const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
) {
jerry_property_descriptor_t desc;
memset(&desc, 0, 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(obj, key, &desc);
jerry_value_free(result);
jerry_value_free(key);
jerry_value_free(desc.getter);
if(setter != NULL) jerry_value_free(desc.setter);
}
/**
* Set a named method (C function) on a JS object.
*
* @param obj Target object (e.g. a prototype).
* @param name Method name.
* @param fn C handler function.
*/
static inline 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);
}
/**
* Format an error message from a JerryScript exception value.
* Caller must ensure buf is large enough.
*/
static inline 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);
}
/**
* Define a named global function.
*
* @param name The name of the function as seen in JavaScript.
* @param fn The C handler function for the method.
*/
static inline 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);
}

Some files were not shown because too many files have changed in this diff Show More