This commit is contained in:
2026-04-28 08:04:01 -05:00
parent 19f2a2c616
commit a41b0e916b
57 changed files with 5023 additions and 3503 deletions
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -4,11 +4,13 @@
# https://opensource.org/licenses/MIT
# Setup
cmake_minimum_required(VERSION 3.18)
cmake_minimum_required(VERSION 4.0)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
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)
+32
View File
@@ -0,0 +1,32 @@
var Cube = {
create: function() {
var e = Entity.create();
e.add(COMPONENT_TYPE_POSITION);
e.position.x = 0;
e.position.y = 0;
e.position.z = 0;
e.add(COMPONENT_TYPE_MESH);
e.add(COMPONENT_TYPE_MATERIAL);
e.material.setColor(colorRed());
return {
_e: e,
update: Cube.update,
dispose: Cube.dispose
};
},
update: function() {
var speed = 3.0;
var dx = inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT);
var dz = inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN);
this._e.position.x += dx * speed * TIME.delta;
this._e.position.z += dz * speed * TIME.delta;
},
dispose: function() {
this._e.dispose();
}
};
Cube;
-25
View File
@@ -1,25 +0,0 @@
local Cube = setmetatable({}, { __index = Entity })
Cube.__index = Cube
function Cube.new()
local self = Entity.new()
setmetatable(self, Cube)
self:add(Entity.POSITION)
self.position.x = 0
self.position.y = 0
self.position.z = 0
self:add(Entity.MESH)
self:add(Entity.MATERIAL)
self.material.color = colorRed()
return self
end
function Cube:update()
local speed = 3.0
local dx = inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT)
local dz = inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN)
self.position.x = self.position.x + dx * speed * TIME.delta
self.position.z = self.position.z + dz * speed * TIME.delta
end
return Cube
+75
View File
@@ -0,0 +1,75 @@
// Default input bindings.
if (typeof PSP !== 'undefined') {
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);
inputBind("triangle", INPUT_ACTION_CONSOLE);
} else if (typeof DOLPHIN !== 'undefined') {
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_CONSOLE);
inputBind("lstick_up", INPUT_ACTION_UP);
inputBind("lstick_down", INPUT_ACTION_DOWN);
inputBind("lstick_left", INPUT_ACTION_LEFT);
inputBind("lstick_right", INPUT_ACTION_RIGHT);
} else if (typeof LINUX !== 'undefined') {
if (typeof INPUT_KEYBOARD !== 'undefined') {
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);
inputBind("`", INPUT_ACTION_CONSOLE);
}
if (typeof INPUT_GAMEPAD !== 'undefined') {
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);
}
if (typeof INPUT_POINTER !== 'undefined') {
inputBind("mouse_x", INPUT_ACTION_POINTERX);
inputBind("mouse_y", INPUT_ACTION_POINTERY);
}
} else {
consolePrint("Unknown platform, no default input bindings set.");
}
Scene.set('scenes/cube.js');
-75
View File
@@ -1,75 +0,0 @@
-- 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)
inputBind("triangle", INPUT_ACTION_CONSOLE)
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_CONSOLE)
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)
inputBind("`", INPUT_ACTION_CONSOLE)
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
Scene.set('scenes/cube.lua')
+29
View File
@@ -0,0 +1,29 @@
var Cube = include('entities/cube.js');
var cam;
var cube;
var SceneCube = {
init: function() {
cam = Entity.create();
cam.add(COMPONENT_TYPE_POSITION);
cam.position.x = 3;
cam.position.y = 3;
cam.position.z = 3;
cam.position.lookAt(0, 0, 0);
cam.add(COMPONENT_TYPE_CAMERA);
cube = Cube.create();
},
update: function() {
cube.update();
},
dispose: function() {
cam.dispose();
cube.dispose();
}
};
SceneCube;
-30
View File
@@ -1,30 +0,0 @@
local Cube = include('entities/cube.lua')
local SceneCube = {}
SceneCube.__index = SceneCube
local cam
local cube
function SceneCube:init()
cam = Entity.new()
cam:add(Entity.POSITION)
cam.position.x = 3
cam.position.y = 3
cam.position.z = 3
cam.position:lookAt(0, 0, 0)
cam:add(Entity.CAMERA)
cube = Cube.new()
end
function SceneCube:update()
cube:update()
end
function SceneCube:dispose()
cam:dispose()
cube:dispose()
end
return SceneCube
+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
include(FetchContent)
FetchContent_Declare(
jerryscript
GIT_REPOSITORY https://github.com/jerryscript-project/jerryscript
GIT_TAG v2.4.0
)
FetchContent_MakeAvailable(jerryscript)
set(jerryscript_FOUND ON)
-22
View File
@@ -22,31 +22,9 @@ set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
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 PUBLIC LUA_USE_C89)
add_library(lua::lua ALIAS liblua)
set(Lua_FOUND TRUE CACHE BOOL "Lua found" FORCE)
# Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
cglm
liblua
m
fat
PkgConfig::zip
-22
View File
@@ -20,31 +20,9 @@ set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
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
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
${SDL2_LIBRARIES}
liblua
cglm
SDL2
SDL2main
+3 -12
View File
@@ -32,18 +32,9 @@ if(NOT yyjson_FOUND)
endif()
endif()
if(NOT Lua_FOUND)
find_package(Lua REQUIRED)
if(Lua_FOUND AND NOT TARGET Lua::Lua)
add_library(Lua::Lua INTERFACE IMPORTED)
set_target_properties(
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)
if(NOT jerryscript_FOUND)
find_package(jerryscript REQUIRED)
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC jerryscript)
endif()
# Includes
@@ -7,76 +7,89 @@
#include "assetscriptloader.h"
#include "assert/assert.h"
#include <stdlib.h>
#include <zip.h>
errorret_t assetScriptLoader(assetfile_t *file) {
assertNotNull(file, "Asset file cannot be NULL");
assertNull(file->zipFile, "Asset file zip handle must be NULL");
assertNull(file->zipFile, "Asset file zip handle must be NULL before open");
assertNotNull(file->output, "Asset file output cannot be NULL");
assetscript_t *script = (assetscript_t *)file->output;
// Open the asset for buffering
errorChain(assetFileOpen(file));
// Request loading
if(lua_load(
script->ctx->luaState,
assetScriptReader,
file,
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);
// Accumulate full source into a dynamically grown buffer.
size_t srcLen = 0;
size_t capacity = ASSET_SCRIPT_CHUNK_SIZE;
char_t *src = (char_t *)malloc(capacity + 1);
if(!src) {
assetFileClose(file);
errorThrow("Out of memory reading script: %s", file->filename);
}
// Now loaded, exec
if(lua_pcall(script->ctx->luaState, 0, LUA_MULTRET, 0) != LUA_OK) {
const char_t *strErr = lua_tostring(script->ctx->luaState, -1);
lua_pop(script->ctx->luaState, 1);
errorThrow("Failed to execute Lua script: %s", strErr);
while(1) {
if(srcLen + ASSET_SCRIPT_CHUNK_SIZE > capacity) {
capacity = srcLen + ASSET_SCRIPT_CHUNK_SIZE;
char_t *tmp = (char_t *)realloc(src, capacity + 1);
if(!tmp) {
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);
}
// Close the file
return assetFileClose(file);
if(script->resultOut != NULL) {
*(script->resultOut) = result;
} else {
jerry_value_free(result);
}
errorret_t assetScriptLoad(const char_t *path, scriptcontext_t *ctx) {
return closeRet;
}
errorret_t assetScriptLoad(
const char_t *path,
scriptcontext_t *ctx,
jerry_value_t *resultOut
) {
assertNotNull(path, "Script path cannot be NULL");
assertNotNull(ctx, "Script context cannot be NULL");
assetscript_t script;
script.ctx = ctx;
assetscript_t scriptData;
scriptData.ctx = ctx;
scriptData.resultOut = resultOut;
return assetLoad(
path,
assetScriptLoader,
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;
return assetLoad(path, assetScriptLoader, NULL, &scriptData);
}
@@ -9,7 +9,7 @@
#include "asset/asset.h"
#include "script/scriptcontext.h"
#define ASSET_SCRIPT_BUFFER_SIZE 1024
#define ASSET_SCRIPT_CHUNK_SIZE 1024
typedef struct {
void *nothing;
@@ -17,11 +17,12 @@ typedef struct {
typedef struct {
scriptcontext_t *ctx;
char_t buffer[ASSET_SCRIPT_BUFFER_SIZE];
jerry_value_t *resultOut;
} assetscript_t;
/**
* Handler for script assets.
* Handler for script assets. Reads the full source, evaluates it with
* JerryScript, and optionally stores the result.
*
* @param file Asset file to load the script from.
* @return Any error that occurs during loading.
@@ -33,16 +34,13 @@ errorret_t assetScriptLoader(assetfile_t *file);
*
* @param path Path to the script asset.
* @param ctx Script context to load the script into.
* @param resultOut Optional out-parameter for the script return value.
* Caller must call jerry_value_free() if non-NULL.
* Pass NULL to discard the return value.
* @return Any error that occurs during loading.
*/
errorret_t assetScriptLoad(const char_t *path, scriptcontext_t *ctx);
/**
* 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);
errorret_t assetScriptLoad(
const char_t *path,
scriptcontext_t *ctx,
jerry_value_t *resultOut
);
+1 -1
View File
@@ -50,7 +50,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
/* Run the init script. */
consolePrint("Engine initialized");
errorChain(scriptContextExecFile(&SCRIPT_MANAGER.mainContext, "init.lua"));
errorChain(scriptContextExecFile(&SCRIPT_MANAGER.mainContext, "init.js", NULL));
errorOk();
}
+46 -71
View File
@@ -10,16 +10,6 @@
#include "util/memory.h"
#include "console/console.h"
static int eventLuaTraceback(lua_State *L) {
const char *msg = lua_tostring(L, 1);
if(msg) {
luaL_traceback(L, L, msg, 1);
} else {
lua_pushliteral(L, "(no error message)");
}
return 1;
}
void eventInit(
event_t *event,
eventlistener_t *array,
@@ -57,19 +47,16 @@ eventsub_t eventSubscribeUser(
"Script event listener context cannot be NULL"
);
assertTrue(
user.script.luaFunctionRef != LUA_NOREF,
user.script.funcValue != 0,
"Script event listener function reference is invalid"
);
} else {
assertUnreachable("Unknown event listener type");
}
// Gen a new ID
eventsub_t id = event->nextId++;
// Did we wrap?
assertTrue(event->nextId != 0, "Event subscription ID overflow");
// Append listener
eventlistener_t *listener = &event->listenerArray[event->listenerCount++];
memoryZero(listener, sizeof(eventlistener_t));
listener->user = user;
@@ -94,49 +81,29 @@ eventsub_t eventSubscribe(
eventsub_t eventSubscribeScriptContext(
event_t *event,
scriptcontext_t *context,
const int functionIndex
jerry_value_t funcValue
) {
assertNotNull(context, "Script context cannot be NULL");
assertTrue(
lua_isfunction(context->luaState, functionIndex),
"Expected function at given index"
);
assertTrue(funcValue != 0, "Script function value is invalid");
// 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;
uint8_t i;
i = 0;
uint8_t i = 0;
do {
if(context->subscribedEvents[i] != event) {
i++;
continue;
}
if(context->subscribedEvents[i] == NULL) break;
if(context->subscribedEvents[i] == event) {
alreadySubbed = true;
break;
}
i++;
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
if(!alreadySubbed) {
i = 0;
do {
if(context->subscribedEvents[i] != NULL) {
i++;
continue;
}
if(context->subscribedEvents[i] == NULL) {
context->subscribedEvents[i] = event;
break;
}
i++;
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
assertTrue(
@@ -148,7 +115,12 @@ eventsub_t eventSubscribeScriptContext(
return eventSubscribeUser(
event,
EVENT_TYPE_SCRIPT,
(eventuserdata_t){ .script = scriptUser }
(eventuserdata_t){
.script = {
.context = context,
.funcValue = funcValue
}
}
);
}
@@ -165,19 +137,13 @@ void eventUnsubscribe(event_t *event, const eventsub_t id) {
continue;
}
// Release Lua registry reference before the slot is overwritten
if(event->listenerArray[index].type == EVENT_TYPE_SCRIPT) {
scriptcontext_t *ctx = event->listenerArray[index].user.script.context;
if(ctx != NULL && ctx->luaState != NULL) {
luaL_unref(
ctx->luaState,
LUA_REGISTRYINDEX,
event->listenerArray[index].user.script.luaFunctionRef
);
jerry_value_t funcVal = event->listenerArray[index].user.script.funcValue;
if(funcVal != 0) {
jerry_value_free(funcVal);
}
}
// Swap with last and shrink
event->listenerArray[index] = event->listenerArray[--event->listenerCount];
return;
} while(index < event->listenerCount);
@@ -199,9 +165,6 @@ void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) {
i++;
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);
} while(i < event->listenerCount);
}
@@ -217,40 +180,52 @@ void eventInvoke(
event->isInvoking = true;
uint16_t i = 0;
eventdata_t data = {
.event = event,
.eventParams = eventParams,
};
uint16_t i = 0;
do {
eventlistener_t *listener = &event->listenerArray[i];
if(listener->type == EVENT_TYPE_C) {
listener->user.c.callback(&data, listener->user.c);
} else if(listener->type == EVENT_TYPE_SCRIPT) {
lua_State *L = listener->user.script.context->luaState;
assertNotNull(L, "Lua state in event listener cannot be NULL");
jerry_value_t funcVal = listener->user.script.funcValue;
assertNotNull((void *)(uintptr_t)funcVal, "Script function value is NULL");
lua_pushcfunction(L, eventLuaTraceback);
int handlerIdx = lua_gettop(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
jerry_value_t callArgs[1];
jerry_length_t argCount = 0;
int numArgs = 0;
if(eventParams != NULL) {
lua_pushlightuserdata(L, (void *)eventParams);
numArgs = 1;
callArgs[0] = jerry_object();
jerry_object_set_native_ptr(
callArgs[0], &JS_PTR_NATIVE_INFO, (void *)eventParams
);
argCount = 1;
}
printf("Invoking Lua event listener with %d argument(s)\n", numArgs);
jerry_value_t result = jerry_call(
funcVal, jerry_undefined(), callArgs, argCount
);
if(lua_pcall(L, numArgs, 0, handlerIdx) != LUA_OK) {
const char_t *strErr = lua_tostring(L, -1);
consolePrint("Error invoking Lua event listener:\n%s\n", strErr);
lua_pop(L, 1);
if(argCount > 0) jerry_value_free(callArgs[0]);
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);
}
lua_pop(L, 1);
jerry_value_free(result);
} else {
assertUnreachable("Unknown event listener type");
}
+1 -1
View File
@@ -84,7 +84,7 @@ eventsub_t eventSubscribe(
eventsub_t eventSubscribeScriptContext(
event_t *event,
scriptcontext_t *context,
const int functionIndex
jerry_value_t funcValue
);
/**
+1 -1
View File
@@ -16,7 +16,7 @@ typedef enum {
typedef struct {
scriptcontext_t *context;
int luaFunctionRef;
jerry_value_t funcValue;
} eventscript_t;
typedef struct eventc_s {
+15 -31
View File
@@ -24,7 +24,7 @@ scene_t SCENE;
errorret_t sceneInit(void) {
memoryZero(&SCENE, sizeof(scene_t));
SCENE.scriptRef = LUA_NOREF;
SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
errorOk();
}
@@ -40,25 +40,22 @@ errorret_t sceneUpdate(void) {
}
if(SCENE.sceneActive) {
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "update"));
errorChain(moduleSceneCall("update"));
}
errorOk();
}
errorret_t sceneRender(void) {
// Get Cameras
entityid_t camEnts[ENTITY_COUNT_MAX];
componentid_t camComps[ENTITY_COUNT_MAX];
entityid_t camCount = componentGetEntitiesWithComponent(
COMPONENT_TYPE_CAMERA, camEnts, camComps
);
// Prep Matricies
mat4 view, proj, model;
if(camCount > 0) {
// Get meshes
entityid_t meshEnts[ENTITY_COUNT_MAX];
componentid_t meshComps[ENTITY_COUNT_MAX];
entityid_t meshCount = componentGetEntitiesWithComponent(
@@ -68,7 +65,6 @@ errorret_t sceneRender(void) {
if(meshCount > 0) {
errorChain(shaderBind(&SHADER_UNLIT));
// For each camera.
for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) {
entityid_t camEnt = camEnts[camIndex];
componentid_t camComp = camComps[camIndex];
@@ -81,15 +77,11 @@ errorret_t sceneRender(void) {
entityCameraGetProjection(camEnt, camComp, proj);
entityPositionGetTransform(camEnt, camPos, view);
// For each mesh.
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;
}
if(mesh == NULL) continue;
componentid_t meshPos = entityGetComponent(
meshEnt, COMPONENT_TYPE_POSITION
@@ -129,7 +121,6 @@ errorret_t sceneRender(void) {
}
}
// Here is where UI will go
glm_ortho(
0.0f, SCREEN.width,
SCREEN.height, 0.0f,
@@ -163,15 +154,11 @@ errorret_t sceneSetImmediate(const char_t *scene) {
}
if(SCENE.sceneActive) {
errorChain(moduleSceneCall(
SCRIPT_MANAGER.mainContext.luaState, "dispose"
));
errorChain(moduleSceneCall("dispose"));
SCENE.sceneActive = false;
}
// Wipe Scene back to a clean table before loading the next file so custom
// methods from the previous scene (e.g. Scene.doThing) cannot bleed through.
moduleSceneReset(SCRIPT_MANAGER.mainContext.luaState);
moduleSceneReset();
stringCopy(
SCENE.sceneCurrent,
@@ -180,22 +167,19 @@ errorret_t sceneSetImmediate(const char_t *scene) {
);
if(scene != NULL) {
lua_State *L = SCRIPT_MANAGER.mainContext.luaState;
jerry_value_t sceneObj = SCENE_SCRIPT_REF_NONE;
errorChain(scriptContextExecFile(
&SCRIPT_MANAGER.mainContext, scene, &sceneObj
));
int32_t stackBase = lua_gettop(L);
errorChain(scriptContextExecFile(&SCRIPT_MANAGER.mainContext, scene));
int32_t nReturns = lua_gettop(L) - stackBase;
if(nReturns < 1 || !lua_istable(L, stackBase + 1)) {
lua_settop(L, stackBase);
errorThrow("Scene '%s' must return a table", scene);
if(!jerry_value_is_object(sceneObj)) {
if(sceneObj != SCENE_SCRIPT_REF_NONE) jerry_value_free(sceneObj);
errorThrow("Scene '%s' must return an object", scene);
}
lua_settop(L, stackBase + 1);
SCENE.scriptRef = luaL_ref(L, LUA_REGISTRYINDEX);
SCENE.scriptRef = sceneObj;
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "init"));
errorChain(moduleSceneCall("init"));
SCENE.sceneActive = true;
}
@@ -211,6 +195,6 @@ void sceneSet(const char_t *scene) {
}
errorret_t sceneDispose(void) {
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "dispose"));
errorChain(moduleSceneCall("dispose"));
errorOk();
}
+10 -18
View File
@@ -14,39 +14,39 @@
typedef struct {
bool_t sceneActive;
int scriptRef;
jerry_value_t scriptRef;
char_t sceneCurrent[ASSET_FILE_PATH_MAX];
char_t sceneNext[ASSET_FILE_PATH_MAX];
} scene_t;
extern scene_t SCENE;
/** Sentinel value meaning no scene script is loaded. */
#define SCENE_SCRIPT_REF_NONE ((jerry_value_t)0)
/**
* Initializes the scene manager
* Initializes the scene manager.
*
* @return Any error state that happened.
*/
errorret_t sceneInit(void);
/**
* Ticks the scene manager, may or may not call the scene's update method
* depending on time state.
* Ticks the scene manager; may call the scene's update method.
*
* @return Any error state that happened.
*/
errorret_t sceneUpdate(void);
/**
* Renders the scene, happens regardless of time.
* Renders the scene.
*
* @return Any error state that happened.
*/
errorret_t sceneRender(void);
/**
* Internal method to immediately switch scenes, this will also delete some of
* the lua state, which can cause problems if called during a lua callback, so
* this is managed by the scene manager.
* Immediately switches scenes, disposing the current one first.
*
* @param scene Scene to switch to (asset file path).
* @return Any error state that happened.
@@ -54,8 +54,7 @@ errorret_t sceneRender(void);
errorret_t sceneSetImmediate(const char_t *scene);
/**
* Requests the scene manager to change scenes when it is next able to. This
* will not guarantee that the scene is valid or without errors.
* Requests a scene change on the next safe opportunity.
*
* @param scene Which scene to set.
*/
@@ -63,14 +62,7 @@ void sceneSet(const char_t *scene);
/**
* Disposes of the current scene.
*
* @return Any error state that happened.
*/
errorret_t sceneDispose(void);
/**
* Lua callback when Scene.set is invoked.
*
* @param L Lua state, expects the first argument to be the scene name (string).
* @return 0, does not return any values to Lua.
*/
int sceneSetLua(lua_State *L);
+147 -89
View File
@@ -10,112 +10,170 @@
#include "display/color.h"
#include "time/time.h"
static int moduleColorIndex(lua_State *L) {
const color_t *color = (const color_t *)luaL_checkudata(L, 1, "color_mt");
assertNotNull(color, "Color struct cannot be NULL.");
const char_t *key = luaL_checkstring(L, 2);
// ---------------------------------------------------------------------------
// Native info / prototype
// ---------------------------------------------------------------------------
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_getmetatable(L, 1);
lua_getfield(L, -1, key);
return 1;
static void freeColorNative(void *ptr, jerry_object_native_info_t *info) {
(void)info;
free(ptr);
}
static const jerry_object_native_info_t COLOR_NATIVE_INFO = {
.free_cb = freeColorNative,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_colorProto = 0;
static int moduleColorNewIndex(lua_State *L) {
color_t *color = (color_t *)luaL_checkudata(L, 1, "color_mt");
assertNotNull(color, "Color struct cannot be NULL.");
const char_t *key = luaL_checkstring(L, 2);
// ---------------------------------------------------------------------------
// Property getters / setters
// ---------------------------------------------------------------------------
if(stringCompare(key, "r") == 0) {
color->r = (colorchannel8_t)luaL_checknumber(L, 3); return 0;
} else if(stringCompare(key, "g") == 0) {
color->g = (colorchannel8_t)luaL_checknumber(L, 3); return 0;
} else if(stringCompare(key, "b") == 0) {
color->b = (colorchannel8_t)luaL_checknumber(L, 3); return 0;
} else if(stringCompare(key, "a") == 0) {
color->a = (colorchannel8_t)luaL_checknumber(L, 3); return 0;
}
luaL_error(L, "color: unknown property '%s'", key);
return 0;
}
static int moduleColorToString(lua_State *L) {
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
JS_FUNC(moduleColorGetR) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
return 1;
return color ? jerry_number(color->r) : jerry_undefined();
}
JS_FUNC(moduleColorSetR) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
if(color && args_count > 0) {
color->r = (colorchannel8_t)jerry_value_as_number(args_p[0]);
}
return jerry_undefined();
}
static int moduleColorCreate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
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.");
JS_FUNC(moduleColorGetG) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
return color ? jerry_number(color->g) : jerry_undefined();
}
JS_FUNC(moduleColorSetG) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
if(color && args_count > 0) {
color->g = (colorchannel8_t)jerry_value_as_number(args_p[0]);
}
return jerry_undefined();
}
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
luaL_getmetatable(L, "color_mt");
lua_setmetatable(L, -2);
color->r = (colorchannel8_t)lua_tonumber(L, 1);
color->g = (colorchannel8_t)lua_tonumber(L, 2);
color->b = (colorchannel8_t)lua_tonumber(L, 3);
color->a = (colorchannel8_t)lua_tonumber(L, 4);
return 1;
JS_FUNC(moduleColorGetB) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
return color ? jerry_number(color->b) : jerry_undefined();
}
JS_FUNC(moduleColorSetB) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
if(color && args_count > 0) {
color->b = (colorchannel8_t)jerry_value_as_number(args_p[0]);
}
return jerry_undefined();
}
static int moduleColorRainbow(lua_State *L) {
JS_FUNC(moduleColorGetA) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
return color ? jerry_number(color->a) : jerry_undefined();
}
JS_FUNC(moduleColorSetA) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
if(color && args_count > 0) {
color->a = (colorchannel8_t)jerry_value_as_number(args_p[0]);
}
return jerry_undefined();
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/**
* Creates a new JS color object wrapping a heap-allocated color_t.
*
* @param color Value to copy into the new object.
* @return Owned jerry_value_t with COLOR_NATIVE_INFO and prototype set.
*/
static inline jerry_value_t moduleColorMakeObject(color_t color) {
color_t *ptr = (color_t *)malloc(sizeof(color_t));
*ptr = color;
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &COLOR_NATIVE_INFO, ptr);
if(s_colorProto != 0) jerry_object_set_proto(obj, s_colorProto);
return obj;
}
/**
* Returns the native color_t pointer stored in a JS value, or NULL.
*
* @param val JS value to inspect.
* @return Pointer to the color_t, or NULL if not a color object.
*/
static inline color_t *moduleColorGetNative(jerry_value_t val) {
return (color_t *)jerry_object_get_native_ptr(val, &COLOR_NATIVE_INFO);
}
// ---------------------------------------------------------------------------
// Constructors
// ---------------------------------------------------------------------------
JS_FUNC(moduleColorCreate) {
JS_REQUIRE_ARGS(4);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
JS_REQUIRE_NUMBER(3);
color_t c;
c.r = (colorchannel8_t)jerry_value_as_number(args_p[0]);
c.g = (colorchannel8_t)jerry_value_as_number(args_p[1]);
c.b = (colorchannel8_t)jerry_value_as_number(args_p[2]);
c.a = (colorchannel8_t)jerry_value_as_number(args_p[3]);
return moduleColorMakeObject(c);
}
JS_FUNC(moduleColorRainbow) {
float_t t = TIME.time * 4.0f;
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);
if(args_count >= 1 && jerry_value_is_number(args_p[0])) {
t += (float_t)jerry_value_as_number(args_p[0]);
}
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);
if(args_count >= 2 && jerry_value_is_number(args_p[1])) {
t *= (float_t)jerry_value_as_number(args_p[1]);
}
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;
luaL_getmetatable(L, "color_mt");
lua_setmetatable(L, -2);
return 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(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
if(luaL_newmetatable(L, "color_mt")) {
lua_pushcfunction(L, moduleColorIndex);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleColorNewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleColorToString);
lua_setfield(L, -2, "__tostring");
}
lua_pop(L, 1);
lua_register(L, "color", moduleColorCreate);
lua_register(L, "colorRainbow", moduleColorRainbow);
luaL_dostring(L, COLOR_SCRIPT);
static void moduleColor(void) {
s_colorProto = jerry_object();
jsDefineProperty(s_colorProto, "r", moduleColorGetR, moduleColorSetR);
jsDefineProperty(s_colorProto, "g", moduleColorGetG, moduleColorSetG);
jsDefineProperty(s_colorProto, "b", moduleColorGetB, moduleColorSetB);
jsDefineProperty(s_colorProto, "a", moduleColorGetA, moduleColorSetA);
jsRegister("color", moduleColorCreate);
jsRegister("colorRainbow", moduleColorRainbow);
jsEvalStr(COLOR_SCRIPT);
}
+38 -22
View File
@@ -8,35 +8,51 @@
#pragma once
#include "script/module/modulebase.h"
#include "display/screen/screen.h"
#include "display/color.h"
static int moduleScreenGetWidth(lua_State *L) {
assertNotNull(L, "Lua state is null");
lua_pushnumber(L, SCREEN.width);
return 1;
JS_FUNC(moduleScreenGetWidth) {
return jerry_number(SCREEN.width);
}
static int moduleScreenGetHeight(lua_State *L) {
assertNotNull(L, "Lua state is null");
lua_pushnumber(L, SCREEN.height);
return 1;
JS_FUNC(moduleScreenGetHeight) {
return jerry_number(SCREEN.height);
}
static 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;
JS_FUNC(moduleScreenSetBackground) {
if(args_count < 1 || !jerry_value_is_object(args_p[0])) {
return JS_THROW("Screen background color must be a color object");
}
color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt");
SCREEN.background = *color;
return 0;
jerry_value_t keyR = jerry_string_sz("r");
jerry_value_t keyG = jerry_string_sz("g");
jerry_value_t keyB = jerry_string_sz("b");
jerry_value_t keyA = jerry_string_sz("a");
jerry_value_t valR = jerry_object_get(args_p[0], keyR);
jerry_value_t valG = jerry_object_get(args_p[0], keyG);
jerry_value_t valB = jerry_object_get(args_p[0], keyB);
jerry_value_t valA = jerry_object_get(args_p[0], keyA);
jerry_value_free(keyR);
jerry_value_free(keyG);
jerry_value_free(keyB);
jerry_value_free(keyA);
SCREEN.background.r = (colorchannel8_t)jerry_value_as_number(valR);
SCREEN.background.g = (colorchannel8_t)jerry_value_as_number(valG);
SCREEN.background.b = (colorchannel8_t)jerry_value_as_number(valB);
SCREEN.background.a = (colorchannel8_t)jerry_value_as_number(valA);
jerry_value_free(valR);
jerry_value_free(valG);
jerry_value_free(valB);
jerry_value_free(valA);
return jerry_undefined();
}
static void moduleScreen(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
lua_register(L, "screenGetWidth", moduleScreenGetWidth);
lua_register(L, "screenGetHeight", moduleScreenGetHeight);
lua_register(L, "screenSetBackground", moduleScreenSetBackground);
static void moduleScreen(void) {
jsRegister("screenGetWidth", moduleScreenGetWidth);
jsRegister("screenGetHeight", moduleScreenGetHeight);
jsRegister("screenSetBackground", moduleScreenSetBackground);
}
+58 -51
View File
@@ -11,80 +11,87 @@
#include "display/shader/shaderunlit.h"
#include "script/module/math/modulemat4.h"
static int moduleShaderBind(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.");
// ---------------------------------------------------------------------------
// Functions
// ---------------------------------------------------------------------------
JS_FUNC(moduleShaderBind) {
JS_REQUIRE_ARGS(1);
shader_t *shader = (shader_t *)jsUnwrapPointer(args_p[0]);
if(!shader) return JS_THROW("shaderBind: expected a shader object");
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 JS_THROW("shaderBind: failed to bind shader");
}
return 0;
return jerry_undefined();
}
static int moduleShaderSetMatrix(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);
mat4 *mat = (mat4 *)luaL_checkudata(l, 3, "mat4_mt");
assertNotNull(mat, "Matrix pointer cannot be NULL.");
JS_FUNC(moduleShaderSetMatrix) {
JS_REQUIRE_ARGS(3);
shader_t *shader = (shader_t *)jsUnwrapPointer(args_p[0]);
if(!shader) return JS_THROW("shaderSetMatrix: expected a shader object");
errorret_t ret = shaderSetMatrix(shader, uniformName, *mat);
JS_REQUIRE_STRING(1);
char_t uniformName[128];
jsToString(args_p[1], uniformName, sizeof(uniformName));
mat4 mat;
if(!moduleMat4Check(args_p[2], mat)) {
return JS_THROW("shaderSetMatrix: third argument must be a mat4");
}
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 JS_THROW("shaderSetMatrix: failed to set shader matrix");
}
return 0;
return jerry_undefined();
}
static 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.");
JS_FUNC(moduleShaderSetTexture) {
JS_REQUIRE_ARGS(2);
shader_t *shader = (shader_t *)jsUnwrapPointer(args_p[0]);
if(!shader) return JS_THROW("shaderSetTexture: expected a shader object");
texture_t *texture;
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;
JS_REQUIRE_STRING(1);
char_t uniformName[128];
jsToString(args_p[1], uniformName, sizeof(uniformName));
texture_t *texture = NULL;
if(
args_count >= 3 &&
!jerry_value_is_null(args_p[2]) &&
!jerry_value_is_undefined(args_p[2])
) {
texture = (texture_t *)jsUnwrapPointer(args_p[2]);
if(!texture) return JS_THROW("shaderSetTexture: third argument must be a texture object or null");
}
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 JS_THROW("shaderSetTexture: failed to set shader texture");
}
return 0;
return jerry_undefined();
}
static void moduleShader(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
lua_pushlightuserdata(L, &SHADER_UNLIT);
lua_setglobal(L, "SHADER_UNLIT");
static void moduleShader(void) {
jerry_value_t shaderUnlitVal = jsWrapPointer(&SHADER_UNLIT);
jsSetValue("SHADER_UNLIT", shaderUnlitVal);
jerry_value_free(shaderUnlitVal);
lua_pushstring(L, SHADER_UNLIT_PROJECTION);
lua_setglobal(L, "SHADER_UNLIT_PROJECTION");
lua_pushstring(L, SHADER_UNLIT_VIEW);
lua_setglobal(L, "SHADER_UNLIT_VIEW");
lua_pushstring(L, SHADER_UNLIT_MODEL);
lua_setglobal(L, "SHADER_UNLIT_MODEL");
lua_pushstring(L, SHADER_UNLIT_TEXTURE);
lua_setglobal(L, "SHADER_UNLIT_TEXTURE");
jsSetString("SHADER_UNLIT_PROJECTION", SHADER_UNLIT_PROJECTION);
jsSetString("SHADER_UNLIT_VIEW", SHADER_UNLIT_VIEW);
jsSetString("SHADER_UNLIT_MODEL", SHADER_UNLIT_MODEL);
jsSetString("SHADER_UNLIT_TEXTURE", SHADER_UNLIT_TEXTURE);
lua_register(L, "shaderBind", moduleShaderBind);
lua_register(L, "shaderSetMatrix", moduleShaderSetMatrix);
lua_register(L, "shaderSetTexture", moduleShaderSetTexture);
jsRegister("shaderBind", moduleShaderBind);
jsRegister("shaderSetMatrix", moduleShaderSetMatrix);
jsRegister("shaderSetTexture", moduleShaderSetTexture);
}
@@ -11,61 +11,98 @@
#include "script/module/math/modulevec2.h"
#include "script/module/math/modulevec4.h"
static int moduleSpriteBatchFlush(lua_State *L) {
assertNotNull(L, "Lua state is null");
// ---------------------------------------------------------------------------
// Functions
// ---------------------------------------------------------------------------
JS_FUNC(moduleSpriteBatchFlush) {
spriteBatchFlush();
return 0;
return jerry_undefined();
}
static int moduleSpriteBatchClear(lua_State *L) {
assertNotNull(L, "Lua state is null");
JS_FUNC(moduleSpriteBatchClear) {
spriteBatchClear();
return 0;
return jerry_undefined();
}
static int moduleSpriteBatchPush(lua_State *L) {
assertNotNull(L, "Lua state is null");
JS_FUNC(moduleSpriteBatchPush) {
JS_REQUIRE_ARGS(2);
vec2 min, max;
moduleVec2Check(L, 1, min);
moduleVec2Check(L, 2, max);
color_t *color = NULL;
if(lua_gettop(L) < 3 || lua_isnil(L, 3)) {
// allow NULL (white)
} else if(lua_isuserdata(L, 3)) {
color = (color_t *)luaL_checkudata(L, 3, "color_mt");
} else {
return luaL_error(L, "Sprite color must be a color struct or nil");
vec2 minPos, maxPos;
if(!moduleVec2Check(args_p[0], minPos)) {
return JS_THROW("spriteBatchPush: first argument must be a vec2");
}
if(!moduleVec2Check(args_p[1], maxPos)) {
return JS_THROW("spriteBatchPush: second argument must be a vec2");
}
#if MESH_ENABLE_COLOR
color_t col = COLOR_WHITE;
if(
args_count >= 3 &&
jerry_value_is_object(args_p[2]) &&
!jerry_value_is_null(args_p[2]) &&
!jerry_value_is_undefined(args_p[2])
) {
jerry_value_t kr = jerry_string_sz("r");
jerry_value_t vr = jerry_object_get(args_p[2], kr);
col.r = (colorchannel8_t)jerry_value_as_number(vr);
jerry_value_free(vr);
jerry_value_free(kr);
jerry_value_t kg = jerry_string_sz("g");
jerry_value_t vg = jerry_object_get(args_p[2], kg);
col.g = (colorchannel8_t)jerry_value_as_number(vg);
jerry_value_free(vg);
jerry_value_free(kg);
jerry_value_t kb = jerry_string_sz("b");
jerry_value_t vb = jerry_object_get(args_p[2], kb);
col.b = (colorchannel8_t)jerry_value_as_number(vb);
jerry_value_free(vb);
jerry_value_free(kb);
jerry_value_t ka = jerry_string_sz("a");
jerry_value_t va = jerry_object_get(args_p[2], ka);
col.a = (colorchannel8_t)jerry_value_as_number(va);
jerry_value_free(va);
jerry_value_free(ka);
}
#endif
float_t u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f;
if(lua_gettop(L) >= 4 && !lua_isnil(L, 4)) {
vec4 uv; moduleVec4Check(L, 4, uv);
if(
args_count >= 4 &&
!jerry_value_is_null(args_p[3]) &&
!jerry_value_is_undefined(args_p[3])
) {
vec4 uv;
if(!moduleVec4Check(args_p[3], uv)) {
return JS_THROW("spriteBatchPush: fourth argument must be a vec4 or null");
}
u0 = uv[0]; v0 = uv[1]; u1 = uv[2]; v1 = uv[3];
}
errorret_t ret = spriteBatchPush(
min[0], min[1], max[0], max[1],
minPos[0], minPos[1], maxPos[0], maxPos[1],
#if MESH_ENABLE_COLOR
color == NULL ? COLOR_WHITE : *color,
col,
#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 JS_THROW("spriteBatchPush: failed to push sprite to batch");
}
return jerry_undefined();
}
return 0;
}
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
static void moduleSpriteBatch(lua_State *L) {
lua_register(L, "spriteBatchFlush", moduleSpriteBatchFlush);
lua_register(L, "spriteBatchClear", moduleSpriteBatchClear);
lua_register(L, "spriteBatchPush", moduleSpriteBatchPush);
static void moduleSpriteBatch(void) {
jsRegister("spriteBatchFlush", moduleSpriteBatchFlush);
jsRegister("spriteBatchClear", moduleSpriteBatchClear);
jsRegister("spriteBatchPush", moduleSpriteBatchPush);
}
+61 -31
View File
@@ -11,62 +11,92 @@
#include "display/spritebatch/spritebatch.h"
#include "script/module/math/modulevec2.h"
static int moduleTextDraw(lua_State *L) {
assertNotNull(L, "Lua state is null");
// ---------------------------------------------------------------------------
// Functions
// ---------------------------------------------------------------------------
vec2 pos; moduleVec2Check(L, 1, pos);
JS_FUNC(moduleTextDraw) {
JS_REQUIRE_ARGS(2);
if(!lua_isstring(L, 2)) {
return luaL_error(L, "Text to draw must be a string");
vec2 pos;
if(!moduleVec2Check(args_p[0], pos)) {
return JS_THROW("textDraw: first argument must be a vec2 position");
}
const char_t *text = (const char_t *)lua_tostring(L, 2);
color_t *color = NULL;
if(lua_gettop(L) < 3 || lua_isnil(L, 3)) {
// allow NULL (white)
} else if(lua_isuserdata(L, 3)) {
color = (color_t *)luaL_checkudata(L, 3, "color_mt");
} else {
return luaL_error(L, "Text color must be a color struct or nil");
JS_REQUIRE_STRING(1);
char_t text[1024];
jsToString(args_p[1], text, sizeof(text));
color_t color = COLOR_WHITE;
if(
args_count >= 3 &&
jerry_value_is_object(args_p[2]) &&
!jerry_value_is_null(args_p[2]) &&
!jerry_value_is_undefined(args_p[2])
) {
jerry_value_t kr = jerry_string_sz("r");
jerry_value_t vr = jerry_object_get(args_p[2], kr);
color.r = (colorchannel8_t)jerry_value_as_number(vr);
jerry_value_free(vr);
jerry_value_free(kr);
jerry_value_t kg = jerry_string_sz("g");
jerry_value_t vg = jerry_object_get(args_p[2], kg);
color.g = (colorchannel8_t)jerry_value_as_number(vg);
jerry_value_free(vg);
jerry_value_free(kg);
jerry_value_t kb = jerry_string_sz("b");
jerry_value_t vb = jerry_object_get(args_p[2], kb);
color.b = (colorchannel8_t)jerry_value_as_number(vb);
jerry_value_free(vb);
jerry_value_free(kb);
jerry_value_t ka = jerry_string_sz("a");
jerry_value_t va = jerry_object_get(args_p[2], ka);
color.a = (colorchannel8_t)jerry_value_as_number(va);
jerry_value_free(va);
jerry_value_free(ka);
}
errorret_t ret = textDraw(
pos[0], pos[1], text,
color == NULL ? COLOR_WHITE : *color,
pos[0], pos[1], text, color,
&DEFAULT_FONT_TILESET,
&DEFAULT_FONT_TEXTURE
);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(L, "Failed to draw text");
return JS_THROW("textDraw: 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");
}
return 0;
return JS_THROW("textDraw: failed to flush sprite batch");
}
static int moduleTextMeasure(lua_State *L) {
assertNotNull(L, "Lua state is null");
if(!lua_isstring(L, 1)) {
return luaL_error(L, "Text to measure must be a string");
return jerry_undefined();
}
const char_t *text = (const char_t *)lua_tostring(L, 1);
JS_FUNC(moduleTextMeasure) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_STRING(0);
char_t text[1024];
jsToString(args_p[0], text, sizeof(text));
int32_t w = 0, h = 0;
textMeasure(text, &DEFAULT_FONT_TILESET, &w, &h);
vec2 size = { (float_t)w, (float_t)h };
moduleVec2Push(L, size);
return 1;
return moduleVec2Push(size);
}
static void moduleText(lua_State *L) {
assertNotNull(L, "Lua state is null");
lua_register(L, "textDraw", moduleTextDraw);
lua_register(L, "textMeasure", moduleTextMeasure);
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
static void moduleText(void) {
jsRegister("textDraw", moduleTextDraw);
jsRegister("textMeasure", moduleTextMeasure);
}
+69 -63
View File
@@ -10,89 +10,95 @@
#include "display/texture/texture.h"
#include "asset/loader/display/assettextureloader.h"
static int moduleTextureIndex(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
// ---------------------------------------------------------------------------
// Native info / prototype
// ---------------------------------------------------------------------------
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.");
static void freeTextureNative(void *ptr, jerry_object_native_info_t *info) {
(void)info;
textureDispose((texture_t *)ptr);
free(ptr);
}
static const jerry_object_native_info_t TEXTURE_NATIVE_INFO = {
.free_cb = freeTextureNative,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_textureProto = 0;
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;
// ---------------------------------------------------------------------------
// Helper
// ---------------------------------------------------------------------------
/**
* Returns the native texture_t pointer stored in a JS value, or NULL.
*
* @param val JS value to inspect.
* @return Pointer to the texture_t, or NULL if not a texture object.
*/
static inline texture_t *moduleTextureGetNative(jerry_value_t val) {
return (texture_t *)jerry_object_get_native_ptr(val, &TEXTURE_NATIVE_INFO);
}
lua_pushnil(l);
return 1;
// ---------------------------------------------------------------------------
// Property getters (read-only)
// ---------------------------------------------------------------------------
JS_FUNC(moduleTextureGetWidth) {
texture_t *tex = (texture_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TEXTURE_NATIVE_INFO
);
return tex ? jerry_number(tex->width) : jerry_undefined();
}
static 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;
JS_FUNC(moduleTextureGetHeight) {
texture_t *tex = (texture_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TEXTURE_NATIVE_INFO
);
return tex ? jerry_number(tex->height) : jerry_undefined();
}
static 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;
}
// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
static int moduleTextureLoad(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
JS_FUNC(moduleTextureLoad) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_STRING(0);
JS_REQUIRE_NUMBER(1);
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;
}
char_t filename[256];
jsToString(args_p[0], filename, sizeof(filename));
const char_t *filename = lua_tostring(l, 1);
assertNotNull(filename, "Filename cannot be NULL.");
assertStrLenMin(filename, 1, "Filename cannot be empty.");
textureformat_t format = (textureformat_t)(int32_t)jerry_value_as_number(args_p[1]);
texture_t *tex = (texture_t *)lua_newuserdata(l, sizeof(texture_t));
texture_t *tex = (texture_t *)malloc(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;
free(tex);
return JS_THROW("textureLoad: failed to load texture");
}
luaL_getmetatable(l, "texture_mt");
lua_setmetatable(l, -2);
return 1;
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &TEXTURE_NATIVE_INFO, tex);
if(s_textureProto != 0) jerry_object_set_proto(obj, s_textureProto);
return obj;
}
static void moduleTexture(lua_State *L) {
assertNotNull(L, "Lua state cannot be null");
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
if(luaL_newmetatable(L, "texture_mt")) {
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");
}
lua_pop(L, 1);
lua_pushnumber(L, TEXTURE_FORMAT_RGBA);
lua_setglobal(L, "TEXTURE_FORMAT_RGBA");
lua_register(L, "textureLoad", moduleTextureLoad);
static void moduleTexture(void) {
s_textureProto = jerry_object();
jsDefineProperty(s_textureProto, "width", moduleTextureGetWidth, NULL);
jsDefineProperty(s_textureProto, "height", moduleTextureGetHeight, NULL);
jsSetInt("TEXTURE_FORMAT_RGBA", (int32_t)TEXTURE_FORMAT_RGBA);
jsRegister("textureLoad", moduleTextureLoad);
}
+106 -71
View File
@@ -11,98 +11,133 @@
#include "asset/loader/display/assettilesetloader.h"
#include "script/module/math/modulevec4.h"
static 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.");
// ---------------------------------------------------------------------------
// Native info / prototype
// ---------------------------------------------------------------------------
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;
static void freeTilesetNative(void *ptr, jerry_object_native_info_t *info) {
(void)info;
free(ptr);
}
static const jerry_object_native_info_t TILESET_NATIVE_INFO = {
.free_cb = freeTilesetNative,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_tilesetProto = 0;
lua_pushnil(l);
return 1;
}
// ---------------------------------------------------------------------------
// Property getters (read-only)
// ---------------------------------------------------------------------------
static 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
JS_FUNC(moduleTilesetGetTileWidth) {
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TILESET_NATIVE_INFO
);
return 1;
return ts ? jerry_number(ts->tileWidth) : jerry_undefined();
}
static int moduleTilesetTileGetUV(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
assertNotNull(ts, "Tileset pointer cannot be NULL.");
uint16_t tileIndex = (uint16_t)luaL_checknumber(l, 2);
vec4 uv; tilesetTileGetUV(ts, tileIndex, uv);
moduleVec4Push(l, uv);
return 1;
JS_FUNC(moduleTilesetGetTileHeight) {
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TILESET_NATIVE_INFO
);
return ts ? jerry_number(ts->tileHeight) : jerry_undefined();
}
static int moduleTilesetPositionGetUV(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
assertNotNull(ts, "Tileset pointer cannot be NULL.");
uint16_t column = (uint16_t)luaL_checknumber(l, 2);
uint16_t row = (uint16_t)luaL_checknumber(l, 3);
vec4 uv; tilesetPositionGetUV(ts, column, row, uv);
moduleVec4Push(l, uv);
return 1;
JS_FUNC(moduleTilesetGetTileCount) {
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TILESET_NATIVE_INFO
);
return ts ? jerry_number(ts->tileCount) : jerry_undefined();
}
static 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;
JS_FUNC(moduleTilesetGetColumns) {
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TILESET_NATIVE_INFO
);
return ts ? jerry_number(ts->columns) : jerry_undefined();
}
const char_t *filename = lua_tostring(l, 1);
assertNotNull(filename, "Filename cannot be NULL.");
assertStrLenMin(filename, 1, "Filename cannot be empty.");
JS_FUNC(moduleTilesetGetRows) {
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TILESET_NATIVE_INFO
);
return ts ? jerry_number(ts->rows) : jerry_undefined();
}
tileset_t *tileset = (tileset_t *)lua_newuserdata(l, sizeof(tileset_t));
memoryZero(tileset, sizeof(tileset_t));
// ---------------------------------------------------------------------------
// Functions
// ---------------------------------------------------------------------------
errorret_t ret = assetTilesetLoad(filename, tileset);
JS_FUNC(modulesTilesetTileGetUV) {
JS_REQUIRE_ARGS(2);
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
args_p[0], &TILESET_NATIVE_INFO
);
if(!ts) return JS_THROW("tilesetTileGetUV: expected a tileset object");
JS_REQUIRE_NUMBER(1);
uint16_t tileIndex = (uint16_t)jerry_value_as_number(args_p[1]);
vec4 uv;
tilesetTileGetUV(ts, tileIndex, uv);
return moduleVec4Push(uv);
}
JS_FUNC(moduleTilesetPositionGetUV) {
JS_REQUIRE_ARGS(3);
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
args_p[0], &TILESET_NATIVE_INFO
);
if(!ts) return JS_THROW("tilesetPositionGetUV: expected a tileset object");
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
uint16_t col = (uint16_t)jerry_value_as_number(args_p[1]);
uint16_t row = (uint16_t)jerry_value_as_number(args_p[2]);
vec4 uv;
tilesetPositionGetUV(ts, col, row, uv);
return moduleVec4Push(uv);
}
JS_FUNC(moduleTilesetLoad) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_STRING(0);
char_t filename[256];
jsToString(args_p[0], filename, sizeof(filename));
tileset_t *ts = (tileset_t *)malloc(sizeof(tileset_t));
memoryZero(ts, sizeof(tileset_t));
errorret_t ret = assetTilesetLoad(filename, ts);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(l, "Failed to load tileset asset: %s", filename);
return 0;
free(ts);
return JS_THROW("tilesetLoad: failed to load tileset");
}
luaL_getmetatable(l, "tileset_mt");
lua_setmetatable(l, -2);
return 1;
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &TILESET_NATIVE_INFO, ts);
if(s_tilesetProto != 0) jerry_object_set_proto(obj, s_tilesetProto);
return obj;
}
static void moduleTileset(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
if(luaL_newmetatable(L, "tileset_mt")) {
lua_pushcfunction(L, moduleTilesetIndex);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleTilesetToString);
lua_setfield(L, -2, "__tostring");
}
lua_pop(L, 1);
static void moduleTileset(void) {
s_tilesetProto = jerry_object();
lua_register(L, "tilesetLoad", moduleTilesetLoad);
lua_register(L, "tilesetTileGetUV", moduleTilesetTileGetUV);
lua_register(L, "tilesetPositionGetUV", moduleTilesetPositionGetUV);
jsDefineProperty(s_tilesetProto, "tileWidth", moduleTilesetGetTileWidth, NULL);
jsDefineProperty(s_tilesetProto, "tileHeight", moduleTilesetGetTileHeight, NULL);
jsDefineProperty(s_tilesetProto, "tileCount", moduleTilesetGetTileCount, NULL);
jsDefineProperty(s_tilesetProto, "columns", moduleTilesetGetColumns, NULL);
jsDefineProperty(s_tilesetProto, "rows", moduleTilesetGetRows, NULL);
jsRegister("tilesetLoad", moduleTilesetLoad);
jsRegister("tilesetTileGetUV", modulesTilesetTileGetUV);
jsRegister("tilesetPositionGetUV", moduleTilesetPositionGetUV);
}
@@ -9,102 +9,67 @@
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/display/entitycamera.h"
#include "moduleentityposition.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entitycam_handle_t;
// ---- camera prototype ----
static jerry_value_t s_camProto = 0;
// -- zNear getter/setter --
JS_FUNC(moduleEntityCameraGetZNear) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
return jerry_number((double)entityCameraGetZNear(eid, cid));
}
JS_FUNC(moduleEntityCameraSetZNear) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
entityCameraSetZNear(eid, cid, (float_t)jerry_value_as_number(args_p[0]));
return jerry_undefined();
}
// -- zFar getter/setter --
JS_FUNC(moduleEntityCameraGetZFar) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
return jerry_number((double)entityCameraGetZFar(eid, cid));
}
JS_FUNC(moduleEntityCameraSetZFar) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
entityCameraSetZFar(eid, cid, (float_t)jerry_value_as_number(args_p[0]));
return jerry_undefined();
}
// -- add function --
/**
* __index metamethod for camera component userdata.
*
* @param L Lua state. Arg 1: entitycam userdata. Arg 2: key string.
* @return 1.
* Adds a camera component to an entity and returns a handle object.
* Arg 0: entity id (number).
*/
static int moduleEntityCameraIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt");
assertNotNull(h, "invalid entitycam userdata");
const char *k = luaL_checkstring(L, 2);
assertStrLenMin(k, 1, "property key cannot be empty");
if(stringCompare(k, "zNear") == 0) {
lua_pushnumber(L, entityCameraGetZNear(h->entityId, h->compId)); return 1;
} else if(stringCompare(k, "zFar") == 0) {
lua_pushnumber(L, entityCameraGetZFar(h->entityId, h->compId)); return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
}
/**
* __newindex metamethod for camera component userdata.
*
* @param L Lua state. Arg 1: entitycam userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleEntityCameraNewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt");
assertNotNull(h, "invalid entitycam userdata");
const char *k = luaL_checkstring(L, 2);
assertStrLenMin(k, 1, "property key cannot be empty");
if(stringCompare(k, "zNear") == 0) {
entityCameraSetZNear(h->entityId, h->compId, (float_t)luaL_checknumber(L, 3));
return 0;
} else if(stringCompare(k, "zFar") == 0) {
entityCameraSetZFar(h->entityId, h->compId, (float_t)luaL_checknumber(L, 3));
return 0;
}
luaL_error(L, "entitycam: unknown property '%s'", k);
return 0;
}
/**
* Adds a camera component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitycam userdata).
*/
static int moduleEntityCameraAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
JS_FUNC(moduleEntityCameraAdd) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_CAMERA);
entitycam_handle_t *h = lua_newuserdata(L, sizeof(entitycam_handle_t));
assertNotNull(h, "failed to allocate entitycam userdata");
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitycam_mt");
lua_setmetatable(L, -2);
return 1;
jerry_value_t handle = makeEntityHandle(id, comp);
if(s_camProto != 0) jerry_object_set_proto(handle, s_camProto);
return handle;
}
/**
* Registers the camera component metatable and entityCameraAdd global.
*
* @param L Lua state.
* Registers the camera component prototype and entityCameraAdd global.
*/
static void moduleEntityCamera(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleEntityCamera(void) {
s_camProto = jerry_object();
luaL_newmetatable(L, "entitycam_mt");
lua_pushcfunction(L, moduleEntityCameraIndex);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityCameraNewIndex);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
jsDefineProperty(s_camProto, "zNear", moduleEntityCameraGetZNear, moduleEntityCameraSetZNear);
jsDefineProperty(s_camProto, "zFar", moduleEntityCameraGetZFar, moduleEntityCameraSetZFar);
lua_register(L, "entityCameraAdd", moduleEntityCameraAdd);
jsRegister("entityCameraAdd", moduleEntityCameraAdd);
}
@@ -9,73 +9,82 @@
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/display/entitymaterial.h"
#include "display/color.h"
#include "moduleentityposition.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entitymat_handle_t;
// ---- material prototype ----
static jerry_value_t s_matProto = 0;
// -- methods --
/**
* __index metamethod for material component userdata.
* Delegates all lookups to the metatable (methods only, no plain properties).
*
* @param L Lua state. Arg 1: entitymat userdata. Arg 2: key string.
* @return 1.
* Sets the material's unlit color from a color object with r, g, b, a
* properties (each a number 0-255).
* Args: colorObj (object).
*/
static int moduleEntityMaterialIndex(lua_State *L) {
lua_getmetatable(L, 1);
lua_getfield(L, -1, luaL_checkstring(L, 2));
return 1;
JS_FUNC(moduleEntityMaterialSetColor) {
JS_REQUIRE_ARGS(1);
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("expected color object");
}
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
jerry_value_t key;
jerry_value_t v;
color_t col;
key = jerry_string_sz("r");
v = jerry_object_get(args_p[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_p[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_p[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_p[0], key);
jerry_value_free(key);
col.a = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
entityMaterialSetColor(eid, cid, col);
return jerry_undefined();
}
/**
* __newindex metamethod for material component userdata.
* Writes color (expects a color_mt userdata). Errors on unknown keys.
*
* @param L Lua state. Arg 1: entitymat userdata. Arg 2: key string. Arg 3: value.
* @return 0.
*/
static int moduleEntityMaterialNewIndex(lua_State *L) {
entitymat_handle_t *h = luaL_checkudata(L, 1, "entitymat_mt");
const char *k = luaL_checkstring(L, 2);
if(stringCompare(k, "color") == 0) {
const color_t *col = (const color_t *)luaL_checkudata(L, 3, "color_mt");
entityMaterialSetColor(h->entityId, h->compId, *col);
return 0;
}
luaL_error(L, "entitymat: unknown property '%s'", k);
return 0;
}
// -- add function --
/**
* Adds a material component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitymat userdata).
* Adds a material component to an entity and returns a handle object.
* Arg 0: entity id (number).
*/
static int moduleEntityMaterialAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
JS_FUNC(moduleEntityMaterialAdd) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MATERIAL);
entitymat_handle_t *h = lua_newuserdata(L, sizeof(entitymat_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitymat_mt");
lua_setmetatable(L, -2);
return 1;
jerry_value_t handle = makeEntityHandle(id, comp);
if(s_matProto != 0) jerry_object_set_proto(handle, s_matProto);
return handle;
}
/**
* Registers the material component metatable and entityMaterialAdd global.
*
* @param L Lua state.
* Registers the material component prototype and entityMaterialAdd global.
*/
static void moduleEntityMaterial(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleEntityMaterial(void) {
s_matProto = jerry_object();
luaL_newmetatable(L, "entitymat_mt");
lua_pushcfunction(L, moduleEntityMaterialIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityMaterialNewIndex); lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
jsDefineMethod(s_matProto, "setColor", moduleEntityMaterialSetColor);
lua_register(L, "entityMaterialAdd", moduleEntityMaterialAdd);
jsRegister("entityMaterialAdd", moduleEntityMaterialAdd);
}
@@ -9,85 +9,65 @@
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/display/entitymesh.h"
#include "moduleentityposition.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entitymesh_handle_t;
// ---- mesh prototype ----
/**
* __index metamethod for mesh component userdata.
* Delegates all lookups to the metatable (methods only, no plain properties).
*
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityMeshIndex(lua_State *L) {
lua_getmetatable(L, 1);
lua_getfield(L, -1, luaL_checkstring(L, 2));
return 1;
}
static jerry_value_t s_meshProto = 0;
// -- methods --
/**
* Generates an XZ-aligned plane mesh owned by this component.
*
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: width. Arg 3: depth.
* @return 0.
* Args: width (number), depth (number).
*/
static int moduleEntityMeshGeneratePlane(lua_State *L) {
entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt");
float_t w = (float_t)luaL_checknumber(L, 2);
float_t d = (float_t)luaL_checknumber(L, 3);
errorret_t err = entityMeshGeneratePlane(h->entityId, h->compId, w, d);
if(err.code != ERROR_OK) luaL_error(L, "entityMesh: generatePlane failed");
return 0;
JS_FUNC(moduleEntityMeshGeneratePlane) {
JS_REQUIRE_ARGS(2);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
float_t w = (float_t)jerry_value_as_number(args_p[0]);
float_t d = (float_t)jerry_value_as_number(args_p[1]);
entityMeshGeneratePlane(eid, cid, w, d);
return jerry_undefined();
}
/**
* Generates a Y-axis capsule mesh owned by this component.
*
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: radius. Arg 3: halfHeight.
* @return 0.
* Args: radius (number), halfHeight (number).
*/
static int moduleEntityMeshGenerateCapsule(lua_State *L) {
entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt");
float_t r = (float_t)luaL_checknumber(L, 2);
float_t hh = (float_t)luaL_checknumber(L, 3);
errorret_t err = entityMeshGenerateCapsule(h->entityId, h->compId, r, hh);
if(err.code != ERROR_OK) luaL_error(L, "entityMesh: generateCapsule failed");
return 0;
JS_FUNC(moduleEntityMeshGenerateCapsule) {
JS_REQUIRE_ARGS(2);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
float_t r = (float_t)jerry_value_as_number(args_p[0]);
float_t hh = (float_t)jerry_value_as_number(args_p[1]);
entityMeshGenerateCapsule(eid, cid, r, hh);
return jerry_undefined();
}
// -- add function --
/**
* Adds a mesh component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitymesh userdata).
* Adds a mesh component to an entity and returns a handle object.
* Arg 0: entity id (number).
*/
static int moduleEntityMeshAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
JS_FUNC(moduleEntityMeshAdd) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MESH);
entitymesh_handle_t *h = lua_newuserdata(L, sizeof(entitymesh_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitymesh_mt");
lua_setmetatable(L, -2);
return 1;
jerry_value_t handle = makeEntityHandle(id, comp);
if(s_meshProto != 0) jerry_object_set_proto(handle, s_meshProto);
return handle;
}
/**
* Registers the mesh component metatable and entityMeshAdd global.
*
* @param L Lua state.
* Registers the mesh component prototype and entityMeshAdd global.
*/
static void moduleEntityMesh(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleEntityMesh(void) {
s_meshProto = jerry_object();
luaL_newmetatable(L, "entitymesh_mt");
lua_pushcfunction(L, moduleEntityMeshIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityMeshGeneratePlane); lua_setfield(L, -2, "generatePlane");
lua_pushcfunction(L, moduleEntityMeshGenerateCapsule); lua_setfield(L, -2, "generateCapsule");
lua_pop(L, 1);
jsDefineMethod(s_meshProto, "generatePlane", moduleEntityMeshGeneratePlane);
jsDefineMethod(s_meshProto, "generateCapsule", moduleEntityMeshGenerateCapsule);
lua_register(L, "entityMeshAdd", moduleEntityMeshAdd);
jsRegister("entityMeshAdd", moduleEntityMeshAdd);
}
@@ -9,210 +9,225 @@
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/physics/entityphysics.h"
#include "moduleentityposition.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entityphysics_handle_t;
// ---- physics prototype ----
/**
* __index metamethod for physics component userdata.
* Reads velX, velY, velZ, onGround (bool), bodyType; falls through to the
* metatable for method lookups.
*
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityPhysicsIndex(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
const char *k = luaL_checkstring(L, 2);
static jerry_value_t s_physProto = 0;
// -- velocity getters/setters --
JS_FUNC(moduleEntityPhysicsGetVelX) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
if(stringCompare(k, "velX") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "velY") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "velZ") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "onGround") == 0) {
lua_pushboolean(L, entityPhysicsIsOnGround(h->entityId, h->compId));
return 1;
} else if(stringCompare(k, "bodyType") == 0) {
lua_pushinteger(L, (lua_Integer)entityPhysicsGetBodyType(h->entityId, h->compId));
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
entityPhysicsGetVelocity(eid, cid, v);
return jerry_number(v[0]);
}
/**
* __newindex metamethod for physics component userdata.
* Writes velX, velY, velZ, bodyType. onGround is read-only. Errors on unknown keys.
*
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: key string. Arg 3: value.
* @return 0.
*/
static int moduleEntityPhysicsNewIndex(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
const char *k = luaL_checkstring(L, 2);
JS_FUNC(moduleEntityPhysicsSetVelX) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
if(stringCompare(k, "velX") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "velY") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "velZ") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "bodyType") == 0) {
entityPhysicsGetVelocity(eid, cid, v);
v[0] = (float_t)jerry_value_as_number(args_p[0]);
entityPhysicsSetVelocity(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPhysicsGetVelY) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPhysicsGetVelocity(eid, cid, v);
return jerry_number(v[1]);
}
JS_FUNC(moduleEntityPhysicsSetVelY) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPhysicsGetVelocity(eid, cid, v);
v[1] = (float_t)jerry_value_as_number(args_p[0]);
entityPhysicsSetVelocity(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPhysicsGetVelZ) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPhysicsGetVelocity(eid, cid, v);
return jerry_number(v[2]);
}
JS_FUNC(moduleEntityPhysicsSetVelZ) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPhysicsGetVelocity(eid, cid, v);
v[2] = (float_t)jerry_value_as_number(args_p[0]);
entityPhysicsSetVelocity(eid, cid, v);
return jerry_undefined();
}
// -- onGround getter (read-only) --
JS_FUNC(moduleEntityPhysicsGetOnGround) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
return jerry_boolean(entityPhysicsIsOnGround(eid, cid));
}
// -- bodyType getter/setter --
JS_FUNC(moduleEntityPhysicsGetBodyType) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
return jerry_number((double)entityPhysicsGetBodyType(eid, cid));
}
JS_FUNC(moduleEntityPhysicsSetBodyType) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
entityPhysicsSetBodyType(
h->entityId, h->compId,
(physicsbodytype_t)luaL_checkinteger(L, 3)
eid, cid,
(physicsbodytype_t)(int32_t)jerry_value_as_number(args_p[0])
);
return 0;
} else if(stringCompare(k, "onGround") == 0) {
luaL_error(L, "entityphysics: onGround is read-only");
return 0;
}
luaL_error(L, "entityphysics: unknown property '%s'", k);
return 0;
return jerry_undefined();
}
// -- methods --
/**
* Applies an immediate velocity impulse to the physics body.
*
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: impulse x, y, z.
* @return 0.
* Args: x, y, z (numbers).
*/
static int moduleEntityPhysicsApplyImpulse(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
JS_FUNC(moduleEntityPhysicsApplyImpulse) {
JS_REQUIRE_ARGS(3);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 impulse = {
(float_t)luaL_checknumber(L, 2),
(float_t)luaL_checknumber(L, 3),
(float_t)luaL_checknumber(L, 4)
(float_t)jerry_value_as_number(args_p[0]),
(float_t)jerry_value_as_number(args_p[1]),
(float_t)jerry_value_as_number(args_p[2])
};
entityPhysicsApplyImpulse(h->entityId, h->compId, impulse);
return 0;
entityPhysicsApplyImpulse(eid, cid, impulse);
return jerry_undefined();
}
/**
* Sets the physics shape to an axis-aligned box.
*
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: half-extents x, y, z.
* @return 0.
* Args: halfX, halfY, halfZ (numbers).
*/
static int moduleEntityPhysicsSetShapeCube(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
JS_FUNC(moduleEntityPhysicsSetShapeCube) {
JS_REQUIRE_ARGS(3);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
physicsshape_t shape;
shape.type = PHYSICS_SHAPE_CUBE;
shape.data.cube.halfExtents[0] = (float_t)luaL_checknumber(L, 2);
shape.data.cube.halfExtents[1] = (float_t)luaL_checknumber(L, 3);
shape.data.cube.halfExtents[2] = (float_t)luaL_checknumber(L, 4);
entityPhysicsSetShape(h->entityId, h->compId, shape);
return 0;
shape.data.cube.halfExtents[0] = (float_t)jerry_value_as_number(args_p[0]);
shape.data.cube.halfExtents[1] = (float_t)jerry_value_as_number(args_p[1]);
shape.data.cube.halfExtents[2] = (float_t)jerry_value_as_number(args_p[2]);
entityPhysicsSetShape(eid, cid, shape);
return jerry_undefined();
}
/**
* Sets the physics shape to a sphere.
*
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: radius.
* @return 0.
* Args: radius (number).
*/
static int moduleEntityPhysicsSetShapeSphere(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
JS_FUNC(moduleEntityPhysicsSetShapeSphere) {
JS_REQUIRE_ARGS(1);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
physicsshape_t shape;
shape.type = PHYSICS_SHAPE_SPHERE;
shape.data.sphere.radius = (float_t)luaL_checknumber(L, 2);
entityPhysicsSetShape(h->entityId, h->compId, shape);
return 0;
shape.data.sphere.radius = (float_t)jerry_value_as_number(args_p[0]);
entityPhysicsSetShape(eid, cid, shape);
return jerry_undefined();
}
/**
* Sets the physics shape to a Y-axis capsule.
*
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: radius. Arg 3: halfHeight.
* @return 0.
* Args: radius (number), halfHeight (number).
*/
static int moduleEntityPhysicsSetShapeCapsule(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
JS_FUNC(moduleEntityPhysicsSetShapeCapsule) {
JS_REQUIRE_ARGS(2);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
physicsshape_t shape;
shape.type = PHYSICS_SHAPE_CAPSULE;
shape.data.capsule.radius = (float_t)luaL_checknumber(L, 2);
shape.data.capsule.halfHeight = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetShape(h->entityId, h->compId, shape);
return 0;
shape.data.capsule.radius = (float_t)jerry_value_as_number(args_p[0]);
shape.data.capsule.halfHeight = (float_t)jerry_value_as_number(args_p[1]);
entityPhysicsSetShape(eid, cid, shape);
return jerry_undefined();
}
/**
* Sets the physics shape to an infinite plane.
*
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: normal x, y, z.
* Arg 5: distance from origin.
* @return 0.
* Args: normalX, normalY, normalZ, distance (numbers).
*/
static int moduleEntityPhysicsSetShapePlane(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
JS_FUNC(moduleEntityPhysicsSetShapePlane) {
JS_REQUIRE_ARGS(4);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
physicsshape_t shape;
shape.type = PHYSICS_SHAPE_PLANE;
shape.data.plane.normal[0] = (float_t)luaL_checknumber(L, 2);
shape.data.plane.normal[1] = (float_t)luaL_checknumber(L, 3);
shape.data.plane.normal[2] = (float_t)luaL_checknumber(L, 4);
shape.data.plane.distance = (float_t)luaL_checknumber(L, 5);
entityPhysicsSetShape(h->entityId, h->compId, shape);
return 0;
shape.data.plane.normal[0] = (float_t)jerry_value_as_number(args_p[0]);
shape.data.plane.normal[1] = (float_t)jerry_value_as_number(args_p[1]);
shape.data.plane.normal[2] = (float_t)jerry_value_as_number(args_p[2]);
shape.data.plane.distance = (float_t)jerry_value_as_number(args_p[3]);
entityPhysicsSetShape(eid, cid, shape);
return jerry_undefined();
}
// -- add function --
/**
* Adds a physics component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entityphysics userdata).
* Adds a physics component to an entity and returns a handle object.
* Arg 0: entity id (number).
*/
static int moduleEntityPhysicsAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
JS_FUNC(moduleEntityPhysicsAdd) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_PHYSICS);
entityphysics_handle_t *h = lua_newuserdata(L, sizeof(entityphysics_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entityphysics_mt");
lua_setmetatable(L, -2);
return 1;
jerry_value_t handle = makeEntityHandle(id, comp);
if(s_physProto != 0) jerry_object_set_proto(handle, s_physProto);
return handle;
}
/**
* Registers the physics component metatable, entityPhysicsAdd global, and
* PHYSICS_BODY_* / PHYSICS_SHAPE_* integer constants.
*
* @param L Lua state.
* Registers the physics component prototype, entityPhysicsAdd global,
* and PHYSICS_BODY_* / PHYSICS_SHAPE_* integer constants.
*/
static void moduleEntityPhysics(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleEntityPhysics(void) {
s_physProto = jerry_object();
luaL_newmetatable(L, "entityphysics_mt");
lua_pushcfunction(L, moduleEntityPhysicsIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityPhysicsNewIndex); lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleEntityPhysicsApplyImpulse); lua_setfield(L, -2, "applyImpulse");
lua_pushcfunction(L, moduleEntityPhysicsSetShapeCube); lua_setfield(L, -2, "setShapeCube");
lua_pushcfunction(L, moduleEntityPhysicsSetShapeSphere); lua_setfield(L, -2, "setShapeSphere");
lua_pushcfunction(L, moduleEntityPhysicsSetShapeCapsule); lua_setfield(L, -2, "setShapeCapsule");
lua_pushcfunction(L, moduleEntityPhysicsSetShapePlane); lua_setfield(L, -2, "setShapePlane");
lua_pop(L, 1);
jsDefineProperty(s_physProto, "velX", moduleEntityPhysicsGetVelX, moduleEntityPhysicsSetVelX);
jsDefineProperty(s_physProto, "velY", moduleEntityPhysicsGetVelY, moduleEntityPhysicsSetVelY);
jsDefineProperty(s_physProto, "velZ", moduleEntityPhysicsGetVelZ, moduleEntityPhysicsSetVelZ);
jsDefineProperty(s_physProto, "onGround", moduleEntityPhysicsGetOnGround, NULL);
jsDefineProperty(s_physProto, "bodyType", moduleEntityPhysicsGetBodyType, moduleEntityPhysicsSetBodyType);
lua_register(L, "entityPhysicsAdd", moduleEntityPhysicsAdd);
jsDefineMethod(s_physProto, "applyImpulse", moduleEntityPhysicsApplyImpulse);
jsDefineMethod(s_physProto, "setShapeCube", moduleEntityPhysicsSetShapeCube);
jsDefineMethod(s_physProto, "setShapeSphere", moduleEntityPhysicsSetShapeSphere);
jsDefineMethod(s_physProto, "setShapeCapsule", moduleEntityPhysicsSetShapeCapsule);
jsDefineMethod(s_physProto, "setShapePlane", moduleEntityPhysicsSetShapePlane);
lua_pushinteger(L, PHYSICS_BODY_STATIC); lua_setglobal(L, "PHYSICS_BODY_STATIC");
lua_pushinteger(L, PHYSICS_BODY_DYNAMIC); lua_setglobal(L, "PHYSICS_BODY_DYNAMIC");
lua_pushinteger(L, PHYSICS_BODY_KINEMATIC); lua_setglobal(L, "PHYSICS_BODY_KINEMATIC");
jsRegister("entityPhysicsAdd", moduleEntityPhysicsAdd);
lua_pushinteger(L, PHYSICS_SHAPE_CUBE); lua_setglobal(L, "PHYSICS_SHAPE_CUBE");
lua_pushinteger(L, PHYSICS_SHAPE_SPHERE); lua_setglobal(L, "PHYSICS_SHAPE_SPHERE");
lua_pushinteger(L, PHYSICS_SHAPE_CAPSULE); lua_setglobal(L, "PHYSICS_SHAPE_CAPSULE");
lua_pushinteger(L, PHYSICS_SHAPE_PLANE); lua_setglobal(L, "PHYSICS_SHAPE_PLANE");
jsSetInt("PHYSICS_BODY_STATIC", PHYSICS_BODY_STATIC);
jsSetInt("PHYSICS_BODY_DYNAMIC", PHYSICS_BODY_DYNAMIC);
jsSetInt("PHYSICS_BODY_KINEMATIC", PHYSICS_BODY_KINEMATIC);
jsSetInt("PHYSICS_SHAPE_CUBE", PHYSICS_SHAPE_CUBE);
jsSetInt("PHYSICS_SHAPE_SPHERE", PHYSICS_SHAPE_SPHERE);
jsSetInt("PHYSICS_SHAPE_CAPSULE", PHYSICS_SHAPE_CAPSULE);
jsSetInt("PHYSICS_SHAPE_PLANE", PHYSICS_SHAPE_PLANE);
}
@@ -10,166 +10,304 @@
#include "entity/entity.h"
#include "entity/component/display/entityposition.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entitypos_handle_t;
// ---- shared entity handle helpers (included once via this file) ----
#ifndef ENTITY_HANDLE_HELPERS
#define ENTITY_HANDLE_HELPERS
/**
* __index metamethod for position component userdata.
* Reads x, y, z, rotX, rotY, rotZ, scaleX, scaleY, scaleZ; falls through to
* the metatable for method lookups.
*
* @param L Lua state. Arg 1: entitypos userdata. Arg 2: key string.
* @return 1.
* Create a plain JS object carrying _eid and _cid number properties.
* The caller is responsible for freeing the returned value.
*/
static int moduleEntityPositionIndex(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v;
if(stringCompare(k, "x") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "y") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "z") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "rotX") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "rotY") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "rotZ") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "scaleX") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "scaleY") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "scaleZ") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
static inline jerry_value_t makeEntityHandle(
entityid_t eid,
componentid_t cid
) {
jerry_value_t obj = jerry_object();
jerry_value_t key;
jerry_value_t val;
key = jerry_string_sz("_eid");
val = jerry_number((double)eid);
jerry_object_set(obj, key, val);
jerry_value_free(val);
jerry_value_free(key);
key = jerry_string_sz("_cid");
val = jerry_number((double)cid);
jerry_object_set(obj, key, val);
jerry_value_free(val);
jerry_value_free(key);
return obj;
}
/**
* __newindex metamethod for position component userdata.
* Writes x, y, z, rotX, rotY, rotZ, scaleX, scaleY, scaleZ and rebuilds the
* transform. Errors on unknown keys.
*
* @param L Lua state. Arg 1: entitypos userdata. Arg 2: key string. Arg 3: number.
* @return 0.
* Extract the entity ID stored in a handle object's _eid property.
*/
static int moduleEntityPositionNewIndex(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
const char *k = luaL_checkstring(L, 2);
static inline entityid_t getEntityId(jerry_value_t handle) {
jerry_value_t key = jerry_string_sz("_eid");
jerry_value_t val = jerry_object_get(handle, key);
jerry_value_free(key);
entityid_t id = (entityid_t)jerry_value_as_number(val);
jerry_value_free(val);
return id;
}
/**
* Extract the component ID stored in a handle object's _cid property.
*/
static inline componentid_t getCompId(jerry_value_t handle) {
jerry_value_t key = jerry_string_sz("_cid");
jerry_value_t val = jerry_object_get(handle, key);
jerry_value_free(key);
componentid_t id = (componentid_t)jerry_value_as_number(val);
jerry_value_free(val);
return id;
}
#endif /* ENTITY_HANDLE_HELPERS */
// ---- position prototype ----
static jerry_value_t s_posProto = 0;
// -- position getters/setters --
JS_FUNC(moduleEntityPositionGetX) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
if(stringCompare(k, "x") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "y") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "z") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotX") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotY") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotZ") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleX") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleY") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleZ") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
entityPositionGetPosition(eid, cid, v);
return jerry_number(v[0]);
}
luaL_error(L, "entitypos: unknown property '%s'", k);
return 0;
JS_FUNC(moduleEntityPositionSetX) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
v[0] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetPosition(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetY) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
return jerry_number(v[1]);
}
JS_FUNC(moduleEntityPositionSetY) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
v[1] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetPosition(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetZ) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
return jerry_number(v[2]);
}
JS_FUNC(moduleEntityPositionSetZ) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
v[2] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetPosition(eid, cid, v);
return jerry_undefined();
}
// -- rotation getters/setters --
JS_FUNC(moduleEntityPositionGetRotX) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
return jerry_number(v[0]);
}
JS_FUNC(moduleEntityPositionSetRotX) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
v[0] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetRotation(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetRotY) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
return jerry_number(v[1]);
}
JS_FUNC(moduleEntityPositionSetRotY) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
v[1] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetRotation(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetRotZ) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
return jerry_number(v[2]);
}
JS_FUNC(moduleEntityPositionSetRotZ) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
v[2] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetRotation(eid, cid, v);
return jerry_undefined();
}
// -- scale getters/setters --
JS_FUNC(moduleEntityPositionGetScaleX) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
return jerry_number(v[0]);
}
JS_FUNC(moduleEntityPositionSetScaleX) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
v[0] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetScale(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetScaleY) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
return jerry_number(v[1]);
}
JS_FUNC(moduleEntityPositionSetScaleY) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
v[1] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetScale(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetScaleZ) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
return jerry_number(v[2]);
}
JS_FUNC(moduleEntityPositionSetScaleZ) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
v[2] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetScale(eid, cid, v);
return jerry_undefined();
}
// -- lookAt method --
/**
* Rotates the entity to face a world-space target point.
* An optional up vector may be provided (args 5-7); defaults to (0,1,0).
*
* @param L Lua state. Arg 1: entitypos userdata. Args 2-4: target x, y, z.
* Args 5-7 (optional): up x, y, z.
* @return 0.
* Args: target x, y, z [, up x, y, z]. Up defaults to (0,1,0).
*/
static int moduleEntityPositionLookAt(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
JS_FUNC(moduleEntityPositionLookAt) {
JS_REQUIRE_ARGS(3);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 target = {
(float_t)luaL_checknumber(L, 2),
(float_t)luaL_checknumber(L, 3),
(float_t)luaL_checknumber(L, 4)
(float_t)jerry_value_as_number(args_p[0]),
(float_t)jerry_value_as_number(args_p[1]),
(float_t)jerry_value_as_number(args_p[2])
};
vec3 up = { 0.0f, 1.0f, 0.0f };
if(lua_gettop(L) >= 7) {
up[0] = (float_t)luaL_checknumber(L, 5);
up[1] = (float_t)luaL_checknumber(L, 6);
up[2] = (float_t)luaL_checknumber(L, 7);
if(args_count >= 6) {
up[0] = (float_t)jerry_value_as_number(args_p[3]);
up[1] = (float_t)jerry_value_as_number(args_p[4]);
up[2] = (float_t)jerry_value_as_number(args_p[5]);
}
vec3 eye;
entityPositionGetPosition(h->entityId, h->compId, eye);
entityPositionLookAt(h->entityId, h->compId, target, up, eye);
return 0;
entityPositionGetPosition(eid, cid, eye);
entityPositionLookAt(eid, cid, target, up, eye);
return jerry_undefined();
}
// -- add function --
/**
* Adds a position component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitypos userdata).
* Adds a position component to an entity and returns a handle object.
* Arg 0: entity id (number).
*/
static int moduleEntityPositionAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
JS_FUNC(moduleEntityPositionAdd) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_POSITION);
entitypos_handle_t *h = lua_newuserdata(L, sizeof(entitypos_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitypos_mt");
lua_setmetatable(L, -2);
return 1;
jerry_value_t handle = makeEntityHandle(id, comp);
if(s_posProto != 0) jerry_object_set_proto(handle, s_posProto);
return handle;
}
/**
* Registers the position component metatable and entityPositionAdd global.
*
* @param L Lua state.
* Registers the position component prototype and entityPositionAdd global.
*/
static void moduleEntityPosition(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleEntityPosition(void) {
s_posProto = jerry_object();
luaL_newmetatable(L, "entitypos_mt");
lua_pushcfunction(L, moduleEntityPositionIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityPositionNewIndex); lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleEntityPositionLookAt); lua_setfield(L, -2, "lookAt");
lua_pop(L, 1);
jsDefineProperty(s_posProto, "x", moduleEntityPositionGetX, moduleEntityPositionSetX);
jsDefineProperty(s_posProto, "y", moduleEntityPositionGetY, moduleEntityPositionSetY);
jsDefineProperty(s_posProto, "z", moduleEntityPositionGetZ, moduleEntityPositionSetZ);
jsDefineProperty(s_posProto, "rotX", moduleEntityPositionGetRotX, moduleEntityPositionSetRotX);
jsDefineProperty(s_posProto, "rotY", moduleEntityPositionGetRotY, moduleEntityPositionSetRotY);
jsDefineProperty(s_posProto, "rotZ", moduleEntityPositionGetRotZ, moduleEntityPositionSetRotZ);
jsDefineProperty(s_posProto, "scaleX", moduleEntityPositionGetScaleX, moduleEntityPositionSetScaleX);
jsDefineProperty(s_posProto, "scaleY", moduleEntityPositionGetScaleY, moduleEntityPositionSetScaleY);
jsDefineProperty(s_posProto, "scaleZ", moduleEntityPositionGetScaleZ, moduleEntityPositionSetScaleZ);
lua_register(L, "entityPositionAdd", moduleEntityPositionAdd);
jsDefineMethod(s_posProto, "lookAt", moduleEntityPositionLookAt);
jsRegister("entityPositionAdd", moduleEntityPositionAdd);
}
+52 -56
View File
@@ -15,87 +15,83 @@
#include "component/moduleentitymaterial.h"
#include "component/moduleentityphysics.h"
// ---- component type constants script ----
#define X(enumName, type, field, init, dispose) \
"COMPONENT_TYPE_" #enumName " = \"" #field "\"\n"
"var COMPONENT_TYPE_" #enumName " = \"" #field "\";\n"
static const char_t *COMPONENT_TYPE_SCRIPT =
#include "entity/componentlist.h"
;
#undef X
// ---- Entity base class script ----
static const char_t *ENTITY_SCRIPT =
"Entity = {}\n"
"Entity.__index = Entity\n"
"var Entity = {};\n"
"Entity.POSITION = COMPONENT_TYPE_POSITION;\n"
"Entity.CAMERA = COMPONENT_TYPE_CAMERA;\n"
"Entity.MESH = COMPONENT_TYPE_MESH;\n"
"Entity.MATERIAL = COMPONENT_TYPE_MATERIAL;\n"
"Entity.PHYSICS = COMPONENT_TYPE_PHYSICS;\n"
"\n"
"Entity.POSITION = COMPONENT_TYPE_POSITION\n"
"Entity.CAMERA = COMPONENT_TYPE_CAMERA\n"
"Entity.MESH = COMPONENT_TYPE_MESH\n"
"Entity.MATERIAL = COMPONENT_TYPE_MATERIAL\n"
"Entity.PHYSICS = COMPONENT_TYPE_PHYSICS\n"
"var _addFns = {};\n"
"_addFns[COMPONENT_TYPE_POSITION] = entityPositionAdd;\n"
"_addFns[COMPONENT_TYPE_CAMERA] = entityCameraAdd;\n"
"_addFns[COMPONENT_TYPE_MESH] = entityMeshAdd;\n"
"_addFns[COMPONENT_TYPE_MATERIAL] = entityMaterialAdd;\n"
"_addFns[COMPONENT_TYPE_PHYSICS] = entityPhysicsAdd;\n"
"\n"
"local _addFns = {\n"
" [COMPONENT_TYPE_POSITION] = entityPositionAdd,\n"
" [COMPONENT_TYPE_CAMERA] = entityCameraAdd,\n"
" [COMPONENT_TYPE_MESH] = entityMeshAdd,\n"
" [COMPONENT_TYPE_MATERIAL] = entityMaterialAdd,\n"
" [COMPONENT_TYPE_PHYSICS] = entityPhysicsAdd,\n"
"}\n"
"Entity.create = function() {\n"
" var self = Object.create(Entity);\n"
" self.id = entityAdd();\n"
" return self;\n"
"};\n"
"\n"
"function Entity.new()\n"
" return setmetatable({ id = entityAdd() }, Entity)\n"
"end\n"
"Entity.add = function(componentType) {\n"
" var fn = _addFns[componentType];\n"
" if(!fn) throw new Error('unknown component type: ' + String(componentType));\n"
" this[componentType] = fn(this.id);\n"
" return this[componentType];\n"
"};\n"
"\n"
"function Entity:add(componentType)\n"
" local fn = _addFns[componentType]\n"
" if not fn then error('unknown component type: ' .. tostring(componentType)) end\n"
" self[componentType] = fn(self.id)\n"
" return self[componentType]\n"
"end\n"
"\n"
"function Entity:dispose()\n"
" entityRemove(self.id)\n"
"end\n"
"Entity.dispose = function() {\n"
" entityRemove(this.id);\n"
"};\n"
;
// ---- module functions ----
/**
* Allocates a new entity and pushes its id onto the Lua stack.
*
* @param L Lua state.
* @return 1 (entity id number).
* Allocates a new entity and returns its id as a JS number.
*/
static int moduleEntityAdd(lua_State *L) {
lua_pushnumber(L, (lua_Number)entityManagerAdd());
return 1;
JS_FUNC(moduleEntityAdd) {
return jerry_number((double)entityManagerAdd());
}
/**
* Disposes the entity with the given id.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 0.
* Arg 0: entity id (number).
*/
static int moduleEntityRemove(lua_State *L) {
entityDispose((entityid_t)luaL_checknumber(L, 1));
return 0;
JS_FUNC(moduleEntityRemove) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityDispose((entityid_t)jerry_value_as_number(args_p[0]));
return jerry_undefined();
}
/**
* Registers all entity and component modules, component type constants, and
* the Entity base class into the Lua state.
*
* @param L Lua state.
* the Entity base object into the JS realm.
*/
static void moduleEntity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleEntity(void) {
moduleEntityPosition();
moduleEntityCamera();
moduleEntityMesh();
moduleEntityMaterial();
moduleEntityPhysics();
moduleEntityPosition(L);
moduleEntityCamera(L);
moduleEntityMesh(L);
moduleEntityMaterial(L);
moduleEntityPhysics(L);
jsRegister("entityAdd", moduleEntityAdd);
jsRegister("entityRemove", moduleEntityRemove);
lua_register(L, "entityAdd", moduleEntityAdd);
lua_register(L, "entityRemove", moduleEntityRemove);
luaL_dostring(L, COMPONENT_TYPE_SCRIPT);
luaL_dostring(L, ENTITY_SCRIPT);
jsEvalStr(COMPONENT_TYPE_SCRIPT);
jsEvalStr(ENTITY_SCRIPT);
}
+22 -37
View File
@@ -9,50 +9,35 @@
#include "script/module/modulebase.h"
#include "event/event.h"
static int moduleEventSubscribe(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
JS_FUNC(moduleEventSubscribe) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_OBJECT(0);
JS_REQUIRE_FUNCTION(1);
scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L);
assertNotNull(context, "Script context cannot be NULL");
event_t *event = (event_t *)jsUnwrapPointer(args_p[0]);
if(event == NULL) return JS_THROW("eventSubscribe: Expected event object");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "eventSubscribe: Expected event pointer as first argument");
return 0;
jerry_value_t funcCopy = jerry_value_copy(args_p[1]);
eventsub_t id = eventSubscribeScriptContext(event, scriptContextCurrent, funcCopy);
return jerry_number((double)id);
}
if(!lua_isfunction(L, 2)) {
luaL_error(L, "eventSubscribe: Expected function as second argument");
return 0;
}
JS_FUNC(moduleEventUnsubscribe) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_OBJECT(0);
JS_REQUIRE_NUMBER(1);
event_t *event = (event_t *)lua_touserdata(L, 1);
assertNotNull(event, "Event cannot be NULL");
event_t *event = (event_t *)jsUnwrapPointer(args_p[0]);
if(event == NULL) return JS_THROW("eventUnsubscribe: Expected event object");
eventsub_t id = eventSubscribeScriptContext(event, context, 2);
lua_pushnumber(L, id);
return 1;
}
static int moduleEventUnsubscribe(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "eventUnsubscribe: Expected event pointer as first argument");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "eventUnsubscribe: Expected subscription ID as second argument");
return 0;
}
event_t *event = (event_t *)lua_touserdata(L, 1);
assertNotNull(event, "Event cannot be NULL");
eventsub_t id = (eventsub_t)lua_tonumber(L, 2);
eventsub_t id = (eventsub_t)jerry_value_as_number(args_p[1]);
eventUnsubscribe(event, id);
return 0;
return jerry_undefined();
}
static void moduleEvent(lua_State *L) {
lua_register(L, "eventSubscribe", moduleEventSubscribe);
lua_register(L, "eventUnsubscribe", moduleEventUnsubscribe);
static void moduleEvent(void) {
jsRegister("eventSubscribe", moduleEventSubscribe);
jsRegister("eventUnsubscribe", moduleEventUnsubscribe);
}
+75 -137
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2025 Dominic Masters
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
@@ -9,198 +9,136 @@
#include "script/module/modulebase.h"
#include "input/input.h"
static int moduleInputIndex(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL");
JS_FUNC(moduleInputBind) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_STRING(0);
JS_REQUIRE_NUMBER(1);
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;
}
static int moduleInputBind(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) {
luaL_error(L, "inputBind: Expected button name as first argument");
return 0;
}
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;
}
char_t strBtn[128];
jsToString(args_p[0], strBtn, sizeof(strBtn));
if(strBtn[0] == '\0') return JS_THROW("inputBind: Button name cannot be empty");
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[1]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputBind: Invalid action ID %d with str %s", action, strBtn);
return 0;
return JS_THROW("inputBind: Invalid action ID");
}
inputbutton_t btn = inputButtonGetByName(strBtn);
if(btn.type == INPUT_BUTTON_TYPE_NONE) {
luaL_error(L, "inputBind: Invalid button name '%s'", strBtn);
return 0;
return JS_THROW("inputBind: Invalid button name");
}
inputBind(btn, action);
return 0;
return jerry_undefined();
}
static 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);
JS_FUNC(moduleInputIsDown) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[0]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputIsDown: Invalid action ID %d", action);
return 0;
return JS_THROW("inputIsDown: Invalid action ID");
}
lua_pushboolean(L, inputIsDown(action));
return 1;
return jerry_boolean(inputIsDown(action));
}
static 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);
JS_FUNC(moduleInputPressed) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[0]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputPressed: Invalid action ID %d", action);
return 0;
return JS_THROW("inputPressed: Invalid action ID");
}
lua_pushboolean(L, inputPressed(action));
return 1;
return jerry_boolean(inputPressed(action));
}
static 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);
JS_FUNC(moduleInputReleased) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[0]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputReleased: Invalid action ID %d", action);
return 0;
return JS_THROW("inputReleased: Invalid action ID");
}
lua_pushboolean(L, inputReleased(action));
return 1;
return jerry_boolean(inputReleased(action));
}
static int moduleInputGetValue(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
JS_FUNC(moduleInputGetValue) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
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);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[0]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputGetValue: Invalid action ID %d", action);
return 0;
return JS_THROW("inputGetValue: Invalid action ID");
}
lua_pushnumber(L, inputGetCurrentValue(action));
return 1;
return jerry_number(inputGetCurrentValue(action));
}
static int moduleInputAxis(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
JS_FUNC(moduleInputAxis) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
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);
const inputaction_t neg = (inputaction_t)jerry_value_as_number(args_p[0]);
const inputaction_t pos = (inputaction_t)jerry_value_as_number(args_p[1]);
if(neg < INPUT_ACTION_NULL || neg >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputAxis: Invalid negative action ID %d", neg);
return 0;
return JS_THROW("inputAxis: Invalid negative action ID");
}
if(pos < INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputAxis: Invalid positive action ID %d", pos);
return 0;
return JS_THROW("inputAxis: Invalid positive action ID");
}
lua_pushnumber(L, inputAxis(neg, pos));
return 1;
return jerry_number(inputAxis(neg, pos));
}
static void moduleInput(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
JS_FUNC(moduleInputGetEventAction) {
JS_REQUIRE_ARGS(1);
luaL_dostring(L, INPUT_ACTION_SCRIPT);
const inputevent_t *event = (const inputevent_t *)jsUnwrapPointer(args_p[0]);
if(event == NULL) return JS_THROW("inputGetEventAction: Expected input event object");
luaL_dostring(L,
return jerry_number(event->action);
}
static void moduleInput(void) {
jsEvalStr(INPUT_ACTION_SCRIPT);
jsEvalStr(
""
#ifdef DUSK_INPUT_KEYBOARD
"INPUT_KEYBOARD = true\n"
"var INPUT_KEYBOARD = true;\n"
#endif
#ifdef DUSK_INPUT_GAMEPAD
"INPUT_GAMEPAD = true\n"
"var INPUT_GAMEPAD = true;\n"
#endif
#ifdef DUSK_INPUT_POINTER
"INPUT_POINTER = true\n"
"var INPUT_POINTER = true;\n"
#endif
#ifdef DUSK_INPUT_TOUCH
"INPUT_TOUCH = true\n"
"var INPUT_TOUCH = true;\n"
#endif
);
if(luaL_newmetatable(L, "input_mt")) {
lua_pushcfunction(L, moduleInputIndex);
lua_setfield(L, -2, "__index");
}
lua_pop(L, 1);
lua_pushlightuserdata(L, &INPUT.eventPressed);
lua_setglobal(L, "INPUT_EVENT_PRESSED");
lua_pushlightuserdata(L, &INPUT.eventReleased);
lua_setglobal(L, "INPUT_EVENT_RELEASED");
lua_register(L, "inputBind", moduleInputBind);
lua_register(L, "inputIsDown", moduleInputIsDown);
lua_register(L, "inputPressed", moduleInputPressed);
lua_register(L, "inputReleased", moduleInputReleased);
lua_register(L, "inputGetValue", moduleInputGetValue);
lua_register(L, "inputAxis", moduleInputAxis);
jerry_value_t evPressed = jsWrapPointer(&INPUT.eventPressed);
jsSetValue("INPUT_EVENT_PRESSED", evPressed);
jerry_value_free(evPressed);
jerry_value_t evReleased = jsWrapPointer(&INPUT.eventReleased);
jsSetValue("INPUT_EVENT_RELEASED", evReleased);
jerry_value_free(evReleased);
jsRegister("inputBind", moduleInputBind);
jsRegister("inputIsDown", moduleInputIsDown);
jsRegister("inputPressed", moduleInputPressed);
jsRegister("inputReleased", moduleInputReleased);
jsRegister("inputGetValue", moduleInputGetValue);
jsRegister("inputAxis", moduleInputAxis);
jsRegister("inputGetEventAction", moduleInputGetEventAction);
}
+39 -46
View File
@@ -9,61 +9,57 @@
#include "script/module/modulebase.h"
#include "locale/localemanager.h"
static int moduleLocaleGetText(lua_State *L) {
if(!lua_isstring(L, 1)) {
luaL_error(L, "Expected message ID as first argument");
return 0;
}
#define MODULE_LOCALE_MAX_ARGS 16
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;
}
JS_FUNC(moduleLocaleGetText) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_STRING(0);
char_t id[256];
jsToString(args_p[0], id, sizeof(id));
if(id[0] == '\0') return JS_THROW("localeGetText: Message ID cannot be empty");
int32_t plural = 0;
int top = lua_gettop(L);
int argStart = 2;
jerry_length_t argStart = 1;
if(top >= 2 && !lua_isnil(L, 2)) {
if(!lua_isnumber(L, 2)) {
luaL_error(L, "Expected plural as second argument");
return 0;
if(args_count >= 2 && !jerry_value_is_undefined(args_p[1])) {
if(!jerry_value_is_number(args_p[1])) {
return JS_THROW("localeGetText: Expected plural as second argument");
}
plural = (int32_t)lua_tonumber(L, 2);
if(plural < 0) {
luaL_error(L, "Plural cannot be negative");
return 0;
}
argStart = 3;
plural = (int32_t)jerry_value_as_number(args_p[1]);
if(plural < 0) return JS_THROW("localeGetText: Plural cannot be negative");
argStart = 2;
}
size_t argCount = (top >= argStart) ? (size_t)(top - argStart + 1) : 0;
#define MODULE_LOCALE_MAX_ARGS 16
assetlocalearg_t argsStack[MODULE_LOCALE_MAX_ARGS];
size_t argCount = (args_count > argStart) ? (size_t)(args_count - argStart) : 0;
if(argCount > MODULE_LOCALE_MAX_ARGS) {
luaL_error(L, "Too many args (max %d)", MODULE_LOCALE_MAX_ARGS);
return 0;
return JS_THROW("localeGetText: Too many format arguments");
}
assetlocalearg_t argsStack[MODULE_LOCALE_MAX_ARGS];
char_t strBufs[MODULE_LOCALE_MAX_ARGS][128];
for(size_t i = 0; i < argCount; ++i) {
int luaIndex = argStart + (int)i;
if(lua_isinteger(L, luaIndex)) {
jerry_value_t arg = args_p[argStart + i];
if(jerry_value_is_number(arg)) {
double num = jerry_value_as_number(arg);
if(num == (double)(int32_t)num) {
argsStack[i].type = ASSET_LOCALE_ARG_INT;
argsStack[i].intValue = (int32_t)lua_tonumber(L, luaIndex);
} else if(lua_isnumber(L, luaIndex)) {
argsStack[i].type = ASSET_LOCALE_ARG_FLOAT;
argsStack[i].floatValue = lua_tonumber(L, luaIndex);
} else if(lua_isstring(L, luaIndex)) {
argsStack[i].type = ASSET_LOCALE_ARG_STRING;
argsStack[i].stringValue = lua_tostring(L, luaIndex);
argsStack[i].intValue = (int32_t)num;
} else {
luaL_error(L, "Unsupported localization argument type");
return 0;
argsStack[i].type = ASSET_LOCALE_ARG_FLOAT;
argsStack[i].floatValue = (float_t)num;
}
} else if(jerry_value_is_string(arg)) {
jsToString(arg, strBufs[i], sizeof(strBufs[i]));
argsStack[i].type = ASSET_LOCALE_ARG_STRING;
argsStack[i].stringValue = strBufs[i];
} else {
return JS_THROW("localeGetText: Unsupported argument type");
}
}
#undef MODULE_LOCALE_MAX_ARGS
char_t buffer[1024];
errorret_t err = localeManagerGetTextArgs(
@@ -71,15 +67,12 @@ static int moduleLocaleGetText(lua_State *L) {
);
if(err.code != ERROR_OK) {
errorCatch(errorPrint(err));
luaL_error(L, "Failed to get localized text for ID '%s'", id);
return 0;
return JS_THROW("localeGetText: Failed to get localized text");
}
lua_pushstring(L, buffer);
return 1;
return jerry_string_sz(buffer);
}
static void moduleLocale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_register(L, "localeGetText", moduleLocaleGetText);
static void moduleLocale(void) {
jsRegister("localeGetText", moduleLocaleGetText);
}
+241 -252
View File
@@ -7,293 +7,282 @@
#pragma once
#include "script/module/modulebase.h"
#include "assert/assert.h"
#include "cglm/cglm.h"
#include "modulevec3.h"
#include "modulevec4.h"
/**
* Pushes a new mat4 userdata onto the Lua stack with the mat4_mt metatable.
*
* @param L Lua state.
* @param m Source matrix to copy.
*/
static void moduleMat4Push(lua_State *L, mat4 m) {
assertNotNull(L, "Lua state cannot be NULL");
// Native info for heap-allocated mat4 (float[4][4])
static void freeMat4Native(void *ptr, jerry_object_native_info_t *info) {
(void)info;
free(ptr);
}
static const jerry_object_native_info_t MAT4_NATIVE_INFO = {
.free_cb = freeMat4Native,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_mat4Proto = 0;
mat4 *u = (mat4 *)lua_newuserdata(L, sizeof(mat4));
glm_mat4_copy(m, *u);
luaL_getmetatable(L, "mat4_mt");
lua_setmetatable(L, -2);
// ---------------------------------------------------------------------------
// Methods
// ---------------------------------------------------------------------------
JS_FUNC(moduleMatMul) {
JS_REQUIRE_ARGS(1);
float_t (*a)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!a) return JS_THROW("mat4.mul: invalid this");
float_t (*b)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
args_p[0], &MAT4_NATIVE_INFO
);
if(!b) return JS_THROW("mat4.mul: argument must be a mat4");
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_mul(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* Reads a mat4 userdata from the given stack index into out.
*
* @param L Lua state.
* @param idx Stack index of the mat4 userdata.
* @param out Destination matrix.
*/
static void moduleMat4Check(lua_State *L, int idx, mat4 out) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, idx, "mat4_mt");
glm_mat4_copy(*m, out);
JS_FUNC(moduleMatTranspose) {
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.transpose: invalid this");
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_transpose_to(m, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* __index metamethod for mat4 userdata.
* Delegates all lookups to the metatable (methods only).
*
* @param L Lua state. Arg 1: mat4 userdata. Arg 2: key string.
* @return 1.
*/
static int moduleMat4Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_getmetatable(L, 1);
lua_getfield(L, -1, luaL_checkstring(L, 2));
lua_remove(L, -2);
return 1;
JS_FUNC(moduleMatInverse) {
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.inverse: invalid this");
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_inv(m, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* __mul metamethod: returns a * b as a new mat4.
*
* @param L Lua state. Arg 1: mat4. Arg 2: mat4.
* @return 1 (new mat4).
*/
static int moduleMat4OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *a = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(a, "invalid mat4 userdata");
mat4 *b = (mat4 *)luaL_checkudata(L, 2, "mat4_mt");
assertNotNull(b, "invalid mat4 userdata");
mat4 r;
glm_mat4_mul(*a, *b, r);
moduleMat4Push(L, r);
return 1;
JS_FUNC(moduleMatDeterminant) {
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.determinant: invalid this");
return jerry_number(glm_mat4_det(m));
}
/**
* __tostring metamethod: returns a placeholder string.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (string).
*/
static int moduleMat4OpToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_pushstring(L, "mat4(...)");
return 1;
JS_FUNC(moduleMatMulVec3) {
JS_REQUIRE_ARGS(1);
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.mulVec3: invalid this");
vec3 vin;
if(!moduleVec3Check(args_p[0], vin)) {
return JS_THROW("mat4.mulVec3: first argument must be a vec3");
}
float_t w = (args_count >= 2 && jerry_value_is_number(args_p[1]))
? (float_t)jerry_value_as_number(args_p[1])
: 1.0f;
vec3 vout;
glm_mat4_mulv3(m, vin, w, vout);
return moduleVec3Push(vout);
}
/**
* Returns the transpose of a mat4 as a new mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (new mat4).
*/
static int moduleMat4Transpose(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
mat4 r;
glm_mat4_transpose_to(*m, r);
moduleMat4Push(L, r);
return 1;
JS_FUNC(moduleMatMulVec4) {
JS_REQUIRE_ARGS(1);
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.mulVec4: invalid this");
vec4 vin;
if(!moduleVec4Check(args_p[0], vin)) {
return JS_THROW("mat4.mulVec4: first argument must be a vec4");
}
vec4 vout;
glm_mat4_mulv(m, vin, vout);
return moduleVec4Push(vout);
}
/**
* Returns the inverse of a mat4 as a new mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (new mat4).
*/
static int moduleMat4Inverse(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
mat4 r;
glm_mat4_inv(*m, r);
moduleMat4Push(L, r);
return 1;
JS_FUNC(moduleMatTranslate) {
JS_REQUIRE_ARGS(1);
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.translate: invalid this");
vec3 tv;
if(!moduleVec3Check(args_p[0], tv)) {
return JS_THROW("mat4.translate: argument must be a vec3");
}
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_copy(m, r);
glm_translate(r, tv);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* Multiplies a mat4 by a vec3, with an optional w component (default 1.0).
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3. Arg 3 (optional): number w.
* @return 1 (new vec3).
*/
static int moduleMat4MulVec3(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
float_t w = lua_gettop(L) >= 3 ? (float_t)luaL_checknumber(L, 3) : 1.0f;
vec3 r;
glm_mat4_mulv3(*m, v, w, r);
moduleVec3Push(L, r);
return 1;
JS_FUNC(moduleMatScale) {
JS_REQUIRE_ARGS(1);
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.scale: invalid this");
vec3 sv;
if(!moduleVec3Check(args_p[0], sv)) {
return JS_THROW("mat4.scale: argument must be a vec3");
}
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_copy(m, r);
glm_scale(r, sv);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* Multiplies a mat4 by a vec4.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleMat4MulVec4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
vec4 v;
moduleVec4Check(L, 2, v);
vec4 r;
glm_mat4_mulv(*m, v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* Returns a copy of a mat4 translated by a vec3.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3.
* @return 1 (new mat4).
*/
static int moduleMat4Translate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
mat4 r;
glm_mat4_copy(*m, r);
glm_translate(r, v);
moduleMat4Push(L, r);
return 1;
}
/**
* Returns a copy of a mat4 scaled by a vec3.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3.
* @return 1 (new mat4).
*/
static int moduleMat4Scale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
mat4 r;
glm_mat4_copy(*m, r);
glm_scale(r, v);
moduleMat4Push(L, r);
return 1;
}
/**
* Returns a new identity mat4.
*
* @param L Lua state.
* @return 1 (new mat4).
*/
static int moduleMat4Identity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 r;
JS_FUNC(moduleMatIdentityMethod) {
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_identity(r);
moduleMat4Push(L, r);
return 1;
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Global constructor / factory functions
// ---------------------------------------------------------------------------
/**
* mat4Identity() - returns a new identity matrix.
*/
JS_FUNC(moduleMatIdentity) {
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_identity(r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* Returns the determinant of a mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (number).
* mat4Perspective(fov, aspect, near, far) - returns a perspective projection
* matrix.
*/
static int moduleMat4Determinant(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
lua_pushnumber(L, (lua_Number)glm_mat4_det(*m));
return 1;
JS_FUNC(moduleMatPerspective) {
JS_REQUIRE_ARGS(4);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
JS_REQUIRE_NUMBER(3);
float_t fov = (float_t)jerry_value_as_number(args_p[0]);
float_t aspect = (float_t)jerry_value_as_number(args_p[1]);
float_t znear = (float_t)jerry_value_as_number(args_p[2]);
float_t zfar = (float_t)jerry_value_as_number(args_p[3]);
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_perspective(fov, aspect, znear, zfar, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* Constructor: creates a new identity mat4.
*
* @param L Lua state.
* @return 1 (new mat4).
* mat4LookAt(eye, center, up) - returns a view matrix.
* eye, center, up are vec3 objects.
*/
static int moduleMat4Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 m;
glm_mat4_identity(m);
moduleMat4Push(L, m);
return 1;
JS_FUNC(moduleMatLookAt) {
JS_REQUIRE_ARGS(3);
vec3 eye, center, up;
if(!moduleVec3Check(args_p[0], eye)) {
return JS_THROW("mat4LookAt: first argument (eye) must be a vec3");
}
if(!moduleVec3Check(args_p[1], center)) {
return JS_THROW("mat4LookAt: second argument (center) must be a vec3");
}
if(!moduleVec3Check(args_p[2], up)) {
return JS_THROW("mat4LookAt: third argument (up) must be a vec3");
}
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_lookat(eye, center, up, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: push a cglm mat4 as a new JS object
// ---------------------------------------------------------------------------
/**
* Registers the mat4 metatable and mat4 constructor global.
* Wraps a copy of a cglm mat4 as a new JerryScript object.
*
* @param L Lua state.
* @param m Source mat4 (float[4][4]) to copy.
* @return Owned jerry_value_t with native ptr set.
*/
static void moduleMat4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!luaL_newmetatable(L, "mat4_mt")) {
lua_pop(L, 1);
return;
static inline jerry_value_t moduleMat4Push(float (*m)[4]) {
float_t (*copy)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_copy(m, copy);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, copy);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
lua_pushcfunction(L, moduleMat4Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleMat4OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleMat4OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleMat4Transpose);
lua_setfield(L, -2, "transpose");
lua_pushcfunction(L, moduleMat4Inverse);
lua_setfield(L, -2, "inverse");
lua_pushcfunction(L, moduleMat4MulVec3);
lua_setfield(L, -2, "mulVec3");
lua_pushcfunction(L, moduleMat4MulVec4);
lua_setfield(L, -2, "mulVec4");
lua_pushcfunction(L, moduleMat4Translate);
lua_setfield(L, -2, "translate");
lua_pushcfunction(L, moduleMat4Scale);
lua_setfield(L, -2, "scale");
lua_pushcfunction(L, moduleMat4Identity);
lua_setfield(L, -2, "identity");
lua_pushcfunction(L, moduleMat4Determinant);
lua_setfield(L, -2, "determinant");
lua_pop(L, 1);
// ---------------------------------------------------------------------------
// Helper: extract mat4 from a JS object
// ---------------------------------------------------------------------------
lua_register(L, "mat4", moduleMat4Create);
/**
* Reads a JerryScript mat4 object into an existing mat4 (float[4][4]).
*
* @param val JS value to read from.
* @param out Destination mat4.
* @return true if val carries a valid mat4 native ptr.
*/
static inline bool_t moduleMat4Check(jerry_value_t val, float (*out)[4]) {
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
val, &MAT4_NATIVE_INFO
);
if(!m) return false;
glm_mat4_copy(m, out);
return true;
}
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
/**
* Creates the mat4 prototype with all methods, then registers the global
* factory functions mat4Identity(), mat4Perspective(), and mat4LookAt().
*/
static void moduleMat4(void) {
s_mat4Proto = jerry_object();
jsDefineMethod(s_mat4Proto, "mul", moduleMatMul);
jsDefineMethod(s_mat4Proto, "transpose", moduleMatTranspose);
jsDefineMethod(s_mat4Proto, "inverse", moduleMatInverse);
jsDefineMethod(s_mat4Proto, "determinant", moduleMatDeterminant);
jsDefineMethod(s_mat4Proto, "mulVec3", moduleMatMulVec3);
jsDefineMethod(s_mat4Proto, "mulVec4", moduleMatMulVec4);
jsDefineMethod(s_mat4Proto, "translate", moduleMatTranslate);
jsDefineMethod(s_mat4Proto, "scale", moduleMatScale);
jsDefineMethod(s_mat4Proto, "identity", moduleMatIdentityMethod);
jsRegister("mat4Identity", moduleMatIdentity);
jsRegister("mat4Perspective", moduleMatPerspective);
jsRegister("mat4LookAt", moduleMatLookAt);
}
+5 -9
View File
@@ -14,14 +14,10 @@
/**
* Registers all math modules: vec2, vec3, vec4, mat4.
*
* @param L Lua state.
*/
static void moduleMath(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
moduleVec2(L);
moduleVec3(L);
moduleVec4(L);
moduleMat4(L);
static void moduleMath(void) {
moduleVec2();
moduleVec3();
moduleVec4();
moduleMat4();
}
+247 -421
View File
@@ -7,441 +7,267 @@
#pragma once
#include "script/module/modulebase.h"
#include "assert/assert.h"
#include "cglm/cglm.h"
// Native info for heap-allocated vec2 (float[2])
static void freeVec2Native(void *ptr, jerry_object_native_info_t *info) {
(void)info;
free(ptr);
}
static const jerry_object_native_info_t VEC2_NATIVE_INFO = {
.free_cb = freeVec2Native,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_vec2Proto = 0;
// ---------------------------------------------------------------------------
// Property getters / setters
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec2GetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
return v ? jerry_number(v[0]) : jerry_undefined();
}
JS_FUNC(moduleVec2SetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(v && args_count > 0) v[0] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec2GetY) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
return v ? jerry_number(v[1]) : jerry_undefined();
}
JS_FUNC(moduleVec2SetY) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(v && args_count > 0) v[1] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
// ---------------------------------------------------------------------------
// Methods
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec2Dot) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!a) return JS_THROW("vec2.dot: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
if(!b) return JS_THROW("vec2.dot: argument must be a vec2");
return jerry_number(glm_vec2_dot(a, b));
}
JS_FUNC(moduleVec2Length) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!v) return JS_THROW("vec2.length: invalid this");
return jerry_number(glm_vec2_norm(v));
}
JS_FUNC(moduleVec2LengthSq) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!v) return JS_THROW("vec2.lengthSq: invalid this");
return jerry_number(glm_vec2_norm2(v));
}
JS_FUNC(moduleVec2Normalize) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!v) return JS_THROW("vec2.normalize: invalid this");
float_t *r = (float_t *)malloc(sizeof(vec2));
glm_vec2_normalize_to(v, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
}
JS_FUNC(moduleVec2Negate) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!v) return JS_THROW("vec2.negate: invalid this");
float_t *r = (float_t *)malloc(sizeof(vec2));
glm_vec2_negate_to(v, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
}
JS_FUNC(moduleVec2Add) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!a) return JS_THROW("vec2.add: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
if(!b) return JS_THROW("vec2.add: argument must be a vec2");
float_t *r = (float_t *)malloc(sizeof(vec2));
glm_vec2_add(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
}
JS_FUNC(moduleVec2Sub) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!a) return JS_THROW("vec2.sub: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
if(!b) return JS_THROW("vec2.sub: argument must be a vec2");
float_t *r = (float_t *)malloc(sizeof(vec2));
glm_vec2_sub(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
}
JS_FUNC(moduleVec2Scale) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!v) return JS_THROW("vec2.scale: invalid this");
float_t s = (float_t)jerry_value_as_number(args_p[0]);
float_t *r = (float_t *)malloc(sizeof(vec2));
glm_vec2_scale(v, s, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
}
JS_FUNC(moduleVec2Lerp) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!a) return JS_THROW("vec2.lerp: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
if(!b) return JS_THROW("vec2.lerp: first argument must be a vec2");
float_t t = (float_t)jerry_value_as_number(args_p[1]);
float_t *r = (float_t *)malloc(sizeof(vec2));
glm_vec2_lerp(a, b, t, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
}
JS_FUNC(moduleVec2Distance) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(!a) return JS_THROW("vec2.distance: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
if(!b) return JS_THROW("vec2.distance: argument must be a vec2");
return jerry_number(glm_vec2_distance(a, b));
}
// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec2Create) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
float_t *v = (float_t *)malloc(sizeof(vec2));
v[0] = (float_t)jerry_value_as_number(args_p[0]);
v[1] = (float_t)jerry_value_as_number(args_p[1]);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, v);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: push a cglm vec2 as a new JS object
// ---------------------------------------------------------------------------
/**
* Pushes a new vec2 userdata onto the Lua stack with the vec2_mt metatable.
* Wraps a copy of a cglm vec2 as a new JerryScript object.
*
* @param L Lua state.
* @param v Source vector to copy.
* @param v Source float[2] to copy.
* @return Owned jerry_value_t with native ptr set.
*/
static void moduleVec2Push(lua_State *L, vec2 v) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *u = (vec2 *)lua_newuserdata(L, sizeof(vec2));
glm_vec2_copy(v, *u);
luaL_getmetatable(L, "vec2_mt");
lua_setmetatable(L, -2);
static inline jerry_value_t moduleVec2Push(const float_t *v) {
float_t *copy = (float_t *)malloc(sizeof(vec2));
copy[0] = v[0];
copy[1] = v[1];
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, copy);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: extract vec2 from a JS object
// ---------------------------------------------------------------------------
/**
* Reads a vec2 userdata from the given stack index into out.
* Reads a JerryScript vec2 object into an existing float[2].
*
* @param L Lua state.
* @param idx Stack index of the vec2 userdata.
* @param out Destination vector.
* @param val JS value to read from.
* @param out Destination float[2].
* @return true if val carries a valid vec2 native ptr.
*/
static void moduleVec2Check(lua_State *L, int idx, vec2 out) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, idx, "vec2_mt");
glm_vec2_copy(*v, out);
static inline bool_t moduleVec2Check(jerry_value_t val, float_t *out) {
float_t *v = (float_t *)jerry_object_get_native_ptr(val, &VEC2_NATIVE_INFO);
if(!v) return false;
out[0] = v[0];
out[1] = v[1];
return true;
}
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
/**
* __index metamethod for vec2 userdata.
* Supports integer indices 1-2 and string keys x, y; falls through to the
* metatable for method lookups.
*
* @param L Lua state. Arg 1: vec2 userdata. Arg 2: integer or string key.
* @return 1.
* Creates the vec2 prototype with x/y getter-setter properties and all
* methods, then registers the global vec2() constructor.
*/
static int moduleVec2Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleVec2(void) {
s_vec2Proto = jerry_object();
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
jsDefineProperty(s_vec2Proto, "x", moduleVec2GetX, moduleVec2SetX);
jsDefineProperty(s_vec2Proto, "y", moduleVec2GetY, moduleVec2SetY);
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
jsDefineMethod(s_vec2Proto, "dot", moduleVec2Dot);
jsDefineMethod(s_vec2Proto, "length", moduleVec2Length);
jsDefineMethod(s_vec2Proto, "lengthSq", moduleVec2LengthSq);
jsDefineMethod(s_vec2Proto, "normalize", moduleVec2Normalize);
jsDefineMethod(s_vec2Proto, "negate", moduleVec2Negate);
jsDefineMethod(s_vec2Proto, "add", moduleVec2Add);
jsDefineMethod(s_vec2Proto, "sub", moduleVec2Sub);
jsDefineMethod(s_vec2Proto, "scale", moduleVec2Scale);
jsDefineMethod(s_vec2Proto, "lerp", moduleVec2Lerp);
jsDefineMethod(s_vec2Proto, "distance", moduleVec2Distance);
if(lua_isnumber(L, 2)) {
lua_Integer i = lua_tointeger(L, 2);
if(i >= 1 && i <= 2) {
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringCompare(key, "x") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringCompare(key, "y") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
}
/**
* __newindex metamethod for vec2 userdata.
* Writes x, y. Errors on unknown keys.
*
* @param L Lua state. Arg 1: vec2 userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleVec2NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
if(stringCompare(key, "x") == 0) {
(*v)[0] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringCompare(key, "y") == 0) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec2: unknown property '%s'", key);
return 0;
}
/**
* __add metamethod: returns a + b as a new vec2.
*
* @param L Lua state. Arg 1: vec2. Arg 2: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2OpAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
vec2 r;
glm_vec2_add(*a, *b, r);
moduleVec2Push(L, r);
return 1;
}
/**
* __sub metamethod: returns a - b as a new vec2.
*
* @param L Lua state. Arg 1: vec2. Arg 2: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2OpSub(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
vec2 r;
glm_vec2_sub(*a, *b, r);
moduleVec2Push(L, r);
return 1;
}
/**
* __mul metamethod: returns v * scalar or scalar * v as a new vec2.
*
* @param L Lua state. Arg 1: vec2 or number. Arg 2: number or vec2.
* @return 1 (new vec2).
*/
static int moduleVec2OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 r;
if(lua_isnumber(L, 1)) {
float_t s = (float_t)lua_tonumber(L, 1);
vec2 *v = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
glm_vec2_scale(*v, s, r);
} else {
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec2_scale(*v, s, r);
}
moduleVec2Push(L, r);
return 1;
}
/**
* __div metamethod: returns v / scalar as a new vec2.
*
* @param L Lua state. Arg 1: vec2. Arg 2: number.
* @return 1 (new vec2).
*/
static int moduleVec2OpDiv(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
vec2 r;
glm_vec2_divs(*v, s, r);
moduleVec2Push(L, r);
return 1;
}
/**
* __unm metamethod: returns -v as a new vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2OpUnm(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
vec2 r;
glm_vec2_negate_to(*v, r);
moduleVec2Push(L, r);
return 1;
}
/**
* __eq metamethod: component-wise equality test.
*
* @param L Lua state. Arg 1: vec2. Arg 2: vec2.
* @return 1 (boolean).
*/
static int moduleVec2OpEq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1]);
return 1;
}
/**
* __tostring metamethod: returns a human-readable representation.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (string).
*/
static int moduleVec2OpToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
char buf[64];
snprintf(buf, sizeof(buf), "vec2(%.3f, %.3f)", (*v)[0], (*v)[1]);
lua_pushstring(L, buf);
return 1;
}
/**
* Returns the dot product of two vec2 values.
*
* @param L Lua state. Arg 1: vec2. Arg 2: vec2.
* @return 1 (number).
*/
static int moduleVec2Dot(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_dot(*a, *b));
return 1;
}
/**
* Returns the length (magnitude) of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (number).
*/
static int moduleVec2Length(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_norm(*v));
return 1;
}
/**
* Returns the squared length of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (number).
*/
static int moduleVec2LengthSq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_norm2(*v));
return 1;
}
/**
* Returns a normalized copy of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2Normalize(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
vec2 r;
glm_vec2_normalize_to(*v, r);
moduleVec2Push(L, r);
return 1;
}
/**
* Linearly interpolates between two vec2 values.
*
* @param L Lua state. Arg 1: vec2 a. Arg 2: vec2 b. Arg 3: number t.
* @return 1 (new vec2).
*/
static int moduleVec2Lerp(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
float_t t = (float_t)luaL_checknumber(L, 3);
vec2 r;
glm_vec2_lerp(*a, *b, t, r);
moduleVec2Push(L, r);
return 1;
}
/**
* Returns the distance between two vec2 points.
*
* @param L Lua state. Arg 1: vec2 a. Arg 2: vec2 b.
* @return 1 (number).
*/
static int moduleVec2Distance(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_distance(*a, *b));
return 1;
}
/**
* Returns a negated copy of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2Negate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
vec2 r;
glm_vec2_negate_to(*v, r);
moduleVec2Push(L, r);
return 1;
}
/**
* Constructor: creates a vec2 from two optional numbers (defaults to 0).
*
* @param L Lua state. Arg 1 (optional): x. Arg 2 (optional): y.
* @return 1 (new vec2).
*/
static int moduleVec2Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 v = {0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
moduleVec2Push(L, v);
return 1;
}
/**
* Registers the vec2 metatable and vec2 constructor global.
*
* @param L Lua state.
*/
static void moduleVec2(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!luaL_newmetatable(L, "vec2_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec2Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec2NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec2OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec2OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec2OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec2OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec2OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec2OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec2OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec2Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec2Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec2LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec2Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec2Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec2Distance);
lua_setfield(L, -2, "distance");
lua_pushcfunction(L, moduleVec2Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec2", moduleVec2Create);
jsRegister("vec2", moduleVec2Create);
}
+283 -455
View File
@@ -7,475 +7,303 @@
#pragma once
#include "script/module/modulebase.h"
#include "assert/assert.h"
#include "cglm/cglm.h"
// Native info for heap-allocated vec3 (float[3])
static void freeVec3Native(void *ptr, jerry_object_native_info_t *info) {
(void)info;
free(ptr);
}
static const jerry_object_native_info_t VEC3_NATIVE_INFO = {
.free_cb = freeVec3Native,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_vec3Proto = 0;
// ---------------------------------------------------------------------------
// Property getters / setters
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec3GetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
return v ? jerry_number(v[0]) : jerry_undefined();
}
JS_FUNC(moduleVec3SetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(v && args_count > 0) v[0] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec3GetY) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
return v ? jerry_number(v[1]) : jerry_undefined();
}
JS_FUNC(moduleVec3SetY) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(v && args_count > 0) v[1] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec3GetZ) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
return v ? jerry_number(v[2]) : jerry_undefined();
}
JS_FUNC(moduleVec3SetZ) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(v && args_count > 0) v[2] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
// ---------------------------------------------------------------------------
// Methods
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec3Dot) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!a) return JS_THROW("vec3.dot: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
if(!b) return JS_THROW("vec3.dot: argument must be a vec3");
return jerry_number(glm_vec3_dot(a, b));
}
JS_FUNC(moduleVec3Cross) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!a) return JS_THROW("vec3.cross: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
if(!b) return JS_THROW("vec3.cross: argument must be a vec3");
float_t *r = (float_t *)malloc(sizeof(vec3));
glm_vec3_cross(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
JS_FUNC(moduleVec3Length) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!v) return JS_THROW("vec3.length: invalid this");
return jerry_number(glm_vec3_norm(v));
}
JS_FUNC(moduleVec3LengthSq) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!v) return JS_THROW("vec3.lengthSq: invalid this");
return jerry_number(glm_vec3_norm2(v));
}
JS_FUNC(moduleVec3Normalize) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!v) return JS_THROW("vec3.normalize: invalid this");
float_t *r = (float_t *)malloc(sizeof(vec3));
glm_vec3_normalize_to(v, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
JS_FUNC(moduleVec3Negate) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!v) return JS_THROW("vec3.negate: invalid this");
float_t *r = (float_t *)malloc(sizeof(vec3));
glm_vec3_negate_to(v, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
JS_FUNC(moduleVec3Add) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!a) return JS_THROW("vec3.add: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
if(!b) return JS_THROW("vec3.add: argument must be a vec3");
float_t *r = (float_t *)malloc(sizeof(vec3));
glm_vec3_add(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
JS_FUNC(moduleVec3Sub) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!a) return JS_THROW("vec3.sub: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
if(!b) return JS_THROW("vec3.sub: argument must be a vec3");
float_t *r = (float_t *)malloc(sizeof(vec3));
glm_vec3_sub(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
JS_FUNC(moduleVec3Scale) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!v) return JS_THROW("vec3.scale: invalid this");
float_t s = (float_t)jerry_value_as_number(args_p[0]);
float_t *r = (float_t *)malloc(sizeof(vec3));
glm_vec3_scale(v, s, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
JS_FUNC(moduleVec3Lerp) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!a) return JS_THROW("vec3.lerp: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
if(!b) return JS_THROW("vec3.lerp: first argument must be a vec3");
float_t t = (float_t)jerry_value_as_number(args_p[1]);
float_t *r = (float_t *)malloc(sizeof(vec3));
glm_vec3_lerp(a, b, t, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
JS_FUNC(moduleVec3Distance) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(!a) return JS_THROW("vec3.distance: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
if(!b) return JS_THROW("vec3.distance: argument must be a vec3");
return jerry_number(glm_vec3_distance(a, b));
}
// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec3Create) {
JS_REQUIRE_ARGS(3);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
float_t *v = (float_t *)malloc(sizeof(vec3));
v[0] = (float_t)jerry_value_as_number(args_p[0]);
v[1] = (float_t)jerry_value_as_number(args_p[1]);
v[2] = (float_t)jerry_value_as_number(args_p[2]);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, v);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: push a cglm vec3 as a new JS object
// ---------------------------------------------------------------------------
/**
* Pushes a new vec3 userdata onto the Lua stack with the vec3_mt metatable.
* Wraps a copy of a cglm vec3 as a new JerryScript object.
*
* @param L Lua state.
* @param v Source vector to copy.
* @param v Source float[3] to copy.
* @return Owned jerry_value_t with native ptr set.
*/
static void moduleVec3Push(lua_State *L, vec3 v) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *u = (vec3 *)lua_newuserdata(L, sizeof(vec3));
glm_vec3_copy(v, *u);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
static inline jerry_value_t moduleVec3Push(const float_t *v) {
float_t *copy = (float_t *)malloc(sizeof(vec3));
copy[0] = v[0];
copy[1] = v[1];
copy[2] = v[2];
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, copy);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: extract vec3 from a JS object
// ---------------------------------------------------------------------------
/**
* Reads a vec3 userdata from the given stack index into out.
* Reads a JerryScript vec3 object into an existing float[3].
*
* @param L Lua state.
* @param idx Stack index of the vec3 userdata.
* @param out Destination vector.
* @param val JS value to read from.
* @param out Destination float[3].
* @return true if val carries a valid vec3 native ptr.
*/
static void moduleVec3Check(lua_State *L, int idx, vec3 out) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, idx, "vec3_mt");
glm_vec3_copy(*v, out);
static inline bool_t moduleVec3Check(jerry_value_t val, float_t *out) {
float_t *v = (float_t *)jerry_object_get_native_ptr(val, &VEC3_NATIVE_INFO);
if(!v) return false;
out[0] = v[0];
out[1] = v[1];
out[2] = v[2];
return true;
}
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
/**
* __index metamethod for vec3 userdata.
* Supports integer indices 1-3 and string keys x, y, z; falls through to the
* metatable for method lookups.
*
* @param L Lua state. Arg 1: vec3 userdata. Arg 2: integer or string key.
* @return 1.
* Creates the vec3 prototype with x/y/z getter-setter properties and all
* methods, then registers the global vec3() constructor.
*/
static int moduleVec3Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleVec3(void) {
s_vec3Proto = jerry_object();
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
jsDefineProperty(s_vec3Proto, "x", moduleVec3GetX, moduleVec3SetX);
jsDefineProperty(s_vec3Proto, "y", moduleVec3GetY, moduleVec3SetY);
jsDefineProperty(s_vec3Proto, "z", moduleVec3GetZ, moduleVec3SetZ);
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
jsDefineMethod(s_vec3Proto, "dot", moduleVec3Dot);
jsDefineMethod(s_vec3Proto, "cross", moduleVec3Cross);
jsDefineMethod(s_vec3Proto, "length", moduleVec3Length);
jsDefineMethod(s_vec3Proto, "lengthSq", moduleVec3LengthSq);
jsDefineMethod(s_vec3Proto, "normalize", moduleVec3Normalize);
jsDefineMethod(s_vec3Proto, "negate", moduleVec3Negate);
jsDefineMethod(s_vec3Proto, "add", moduleVec3Add);
jsDefineMethod(s_vec3Proto, "sub", moduleVec3Sub);
jsDefineMethod(s_vec3Proto, "scale", moduleVec3Scale);
jsDefineMethod(s_vec3Proto, "lerp", moduleVec3Lerp);
jsDefineMethod(s_vec3Proto, "distance", moduleVec3Distance);
if(lua_isnumber(L, 2)) {
lua_Integer i = lua_tointeger(L, 2);
if(i >= 1 && i <= 3) {
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringCompare(key, "x") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringCompare(key, "y") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
if(stringCompare(key, "z") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[2]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
}
/**
* __newindex metamethod for vec3 userdata.
* Writes x, y, z. Errors on unknown keys.
*
* @param L Lua state. Arg 1: vec3 userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleVec3NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
if(stringCompare(key, "x") == 0) {
(*v)[0] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringCompare(key, "y") == 0) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringCompare(key, "z") == 0) {
(*v)[2] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec3: unknown property '%s'", key);
return 0;
}
/**
* __add metamethod: returns a + b as a new vec3.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3OpAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
vec3 r;
glm_vec3_add(*a, *b, r);
moduleVec3Push(L, r);
return 1;
}
/**
* __sub metamethod: returns a - b as a new vec3.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3OpSub(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
vec3 r;
glm_vec3_sub(*a, *b, r);
moduleVec3Push(L, r);
return 1;
}
/**
* __mul metamethod: returns v * scalar or scalar * v as a new vec3.
*
* @param L Lua state. Arg 1: vec3 or number. Arg 2: number or vec3.
* @return 1 (new vec3).
*/
static int moduleVec3OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 r;
if(lua_isnumber(L, 1)) {
float_t s = (float_t)lua_tonumber(L, 1);
vec3 *v = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
glm_vec3_scale(*v, s, r);
} else {
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec3_scale(*v, s, r);
}
moduleVec3Push(L, r);
return 1;
}
/**
* __div metamethod: returns v / scalar as a new vec3.
*
* @param L Lua state. Arg 1: vec3. Arg 2: number.
* @return 1 (new vec3).
*/
static int moduleVec3OpDiv(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
vec3 r;
glm_vec3_divs(*v, s, r);
moduleVec3Push(L, r);
return 1;
}
/**
* __unm metamethod: returns -v as a new vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3OpUnm(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
vec3 r;
glm_vec3_negate_to(*v, r);
moduleVec3Push(L, r);
return 1;
}
/**
* __eq metamethod: component-wise equality test.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (boolean).
*/
static int moduleVec3OpEq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] && (*a)[2] == (*b)[2]);
return 1;
}
/**
* __tostring metamethod: returns a human-readable representation.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (string).
*/
static int moduleVec3OpToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
char buf[80];
snprintf(buf, sizeof(buf), "vec3(%.3f, %.3f, %.3f)", (*v)[0], (*v)[1], (*v)[2]);
lua_pushstring(L, buf);
return 1;
}
/**
* Returns the dot product of two vec3 values.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (number).
*/
static int moduleVec3Dot(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_dot(*a, *b));
return 1;
}
/**
* Returns the cross product of two vec3 values as a new vec3.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3Cross(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
vec3 r;
glm_vec3_cross(*a, *b, r);
moduleVec3Push(L, r);
return 1;
}
/**
* Returns the length (magnitude) of a vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (number).
*/
static int moduleVec3Length(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_norm(*v));
return 1;
}
/**
* Returns the squared length of a vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (number).
*/
static int moduleVec3LengthSq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_norm2(*v));
return 1;
}
/**
* Returns a normalized copy of a vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3Normalize(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
vec3 r;
glm_vec3_normalize_to(*v, r);
moduleVec3Push(L, r);
return 1;
}
/**
* Linearly interpolates between two vec3 values.
*
* @param L Lua state. Arg 1: vec3 a. Arg 2: vec3 b. Arg 3: number t.
* @return 1 (new vec3).
*/
static int moduleVec3Lerp(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
float_t t = (float_t)luaL_checknumber(L, 3);
vec3 r;
glm_vec3_lerp(*a, *b, t, r);
moduleVec3Push(L, r);
return 1;
}
/**
* Returns the distance between two vec3 points.
*
* @param L Lua state. Arg 1: vec3 a. Arg 2: vec3 b.
* @return 1 (number).
*/
static int moduleVec3Distance(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_distance(*a, *b));
return 1;
}
/**
* Returns a negated copy of a vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3Negate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
vec3 r;
glm_vec3_negate_to(*v, r);
moduleVec3Push(L, r);
return 1;
}
/**
* Constructor: creates a vec3 from three optional numbers (defaults to 0).
*
* @param L Lua state. Arg 1 (optional): x. Arg 2 (optional): y. Arg 3 (optional): z.
* @return 1 (new vec3).
*/
static int moduleVec3Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 v = {0, 0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
if(top >= 3) v[2] = (float_t)luaL_checknumber(L, 3);
moduleVec3Push(L, v);
return 1;
}
/**
* Registers the vec3 metatable and vec3 constructor global.
*
* @param L Lua state.
*/
static void moduleVec3(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!luaL_newmetatable(L, "vec3_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec3Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec3NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec3OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec3OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec3OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec3OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec3OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec3OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec3OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec3Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec3Cross);
lua_setfield(L, -2, "cross");
lua_pushcfunction(L, moduleVec3Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec3LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec3Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec3Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec3Distance);
lua_setfield(L, -2, "distance");
lua_pushcfunction(L, moduleVec3Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec3", moduleVec3Create);
jsRegister("vec3", moduleVec3Create);
}
+333 -420
View File
@@ -7,445 +7,358 @@
#pragma once
#include "script/module/modulebase.h"
#include "assert/assert.h"
#include "cglm/cglm.h"
/**
* Pushes a new vec4 userdata onto the Lua stack with the vec4_mt metatable.
*
* @param L Lua state.
* @param v Source vector to copy.
*/
static void moduleVec4Push(lua_State *L, vec4 v) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *u = (vec4 *)lua_newuserdata(L, sizeof(vec4));
glm_vec4_copy(v, *u);
luaL_getmetatable(L, "vec4_mt");
lua_setmetatable(L, -2);
// Native info for heap-allocated vec4 (float[4])
static void freeVec4Native(void *ptr, jerry_object_native_info_t *info) {
(void)info;
free(ptr);
}
static const jerry_object_native_info_t VEC4_NATIVE_INFO = {
.free_cb = freeVec4Native,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_vec4Proto = 0;
/**
* Reads a vec4 userdata from the given stack index into out.
*
* @param L Lua state.
* @param idx Stack index of the vec4 userdata.
* @param out Destination vector.
*/
static void moduleVec4Check(lua_State *L, int idx, vec4 out) {
assertNotNull(L, "Lua state cannot be NULL");
// ---------------------------------------------------------------------------
// Property getters / setters (x/u0, y/v0, z/u1, w/v1)
// ---------------------------------------------------------------------------
vec4 *v = (vec4 *)luaL_checkudata(L, idx, "vec4_mt");
glm_vec4_copy(*v, out);
}
/**
* __index metamethod for vec4 userdata.
* Supports integer indices 1-4 and string keys x/u0, y/v0, z/u1, w/v1;
* falls through to the metatable for method lookups.
*
* @param L Lua state. Arg 1: vec4 userdata. Arg 2: integer or string key.
* @return 1.
*/
static int moduleVec4Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
if(lua_isnumber(L, 2)) {
lua_Integer i = lua_tointeger(L, 2);
if(i >= 1 && i <= 4) {
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringEquals(key, "x") || stringEquals(key, "u0")) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringEquals(key, "y") || stringEquals(key, "v0")) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
if(stringEquals(key, "z") || stringEquals(key, "u1")) {
lua_pushnumber(L, (lua_Number)(*v)[2]);
return 1;
}
if(stringEquals(key, "w") || stringEquals(key, "v1")) {
lua_pushnumber(L, (lua_Number)(*v)[3]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
}
/**
* __newindex metamethod for vec4 userdata.
* Writes x/u0, y/v0, z/u1, w/v1. Errors on unknown keys.
*
* @param L Lua state. Arg 1: vec4 userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleVec4NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
if(stringEquals(key, "x") || stringEquals(key, "u0")) {
(*v)[0] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "y") || stringEquals(key, "v0")) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "z") || stringEquals(key, "u1")) {
(*v)[2] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "w") || stringEquals(key, "v1")) {
(*v)[3] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec4: unknown property '%s'", key);
return 0;
}
/**
* __add metamethod: returns a + b as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
vec4 r;
glm_vec4_add(*a, *b, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __sub metamethod: returns a - b as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpSub(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
vec4 r;
glm_vec4_sub(*a, *b, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __mul metamethod: returns v * scalar or scalar * v as a new vec4.
*
* @param L Lua state. Arg 1: vec4 or number. Arg 2: number or vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 r;
if(lua_isnumber(L, 1)) {
float_t s = (float_t)lua_tonumber(L, 1);
vec4 *v = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
glm_vec4_scale(*v, s, r);
} else {
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec4_scale(*v, s, r);
}
moduleVec4Push(L, r);
return 1;
}
/**
* __div metamethod: returns v / scalar as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: number.
* @return 1 (new vec4).
*/
static int moduleVec4OpDiv(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
vec4 r;
glm_vec4_divs(*v, s, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __unm metamethod: returns -v as a new vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpUnm(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_negate_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __eq metamethod: component-wise equality test.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (boolean).
*/
static int moduleVec4OpEq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
lua_pushboolean(L,
(*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] &&
(*a)[2] == (*b)[2] && (*a)[3] == (*b)[3]
JS_FUNC(moduleVec4GetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return 1;
return v ? jerry_number(v[0]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[0] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetY) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[1]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetY) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[1] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetZ) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[2]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetZ) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[2] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetW) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[3]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetW) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[3] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
// u0/v0/u1/v1 aliases share the same backing floats
JS_FUNC(moduleVec4GetU0) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[0]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetU0) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[0] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetV0) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[1]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetV0) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[1] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetU1) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[2]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetU1) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[2] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetV1) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[3]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetV1) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[3] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
// ---------------------------------------------------------------------------
// Methods
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec4Dot) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!a) return JS_THROW("vec4.dot: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC4_NATIVE_INFO);
if(!b) return JS_THROW("vec4.dot: argument must be a vec4");
return jerry_number(glm_vec4_dot(a, b));
}
JS_FUNC(moduleVec4Length) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.length: invalid this");
return jerry_number(glm_vec4_norm(v));
}
JS_FUNC(moduleVec4LengthSq) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.lengthSq: invalid this");
return jerry_number(glm_vec4_norm2(v));
}
JS_FUNC(moduleVec4Normalize) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.normalize: invalid this");
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_normalize_to(v, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Negate) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.negate: invalid this");
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_negate_to(v, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Add) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!a) return JS_THROW("vec4.add: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC4_NATIVE_INFO);
if(!b) return JS_THROW("vec4.add: argument must be a vec4");
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_add(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Sub) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!a) return JS_THROW("vec4.sub: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC4_NATIVE_INFO);
if(!b) return JS_THROW("vec4.sub: argument must be a vec4");
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_sub(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Scale) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.scale: invalid this");
float_t s = (float_t)jerry_value_as_number(args_p[0]);
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_scale(v, s, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Lerp) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!a) return JS_THROW("vec4.lerp: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC4_NATIVE_INFO);
if(!b) return JS_THROW("vec4.lerp: first argument must be a vec4");
float_t t = (float_t)jerry_value_as_number(args_p[1]);
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_lerp(a, b, t, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec4Create) {
JS_REQUIRE_ARGS(4);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
JS_REQUIRE_NUMBER(3);
float_t *v = (float_t *)malloc(sizeof(vec4));
v[0] = (float_t)jerry_value_as_number(args_p[0]);
v[1] = (float_t)jerry_value_as_number(args_p[1]);
v[2] = (float_t)jerry_value_as_number(args_p[2]);
v[3] = (float_t)jerry_value_as_number(args_p[3]);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, v);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: push a cglm vec4 as a new JS object
// ---------------------------------------------------------------------------
/**
* __tostring metamethod: returns a human-readable representation.
* Wraps a copy of a cglm vec4 as a new JerryScript object.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (string).
* @param v Source float[4] to copy.
* @return Owned jerry_value_t with native ptr set.
*/
static int moduleVec4OpToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
char buf[96];
snprintf(buf, sizeof(buf), "vec4(%.3f, %.3f, %.3f, %.3f)", (*v)[0], (*v)[1], (*v)[2], (*v)[3]);
lua_pushstring(L, buf);
return 1;
static inline jerry_value_t moduleVec4Push(const float_t *v) {
float_t *copy = (float_t *)malloc(sizeof(vec4));
copy[0] = v[0];
copy[1] = v[1];
copy[2] = v[2];
copy[3] = v[3];
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, copy);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: extract vec4 from a JS object
// ---------------------------------------------------------------------------
/**
* Returns the dot product of two vec4 values.
* Reads a JerryScript vec4 object into an existing float[4].
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (number).
* @param val JS value to read from.
* @param out Destination float[4].
* @return true if val carries a valid vec4 native ptr.
*/
static int moduleVec4Dot(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
lua_pushnumber(L, (lua_Number)glm_vec4_dot(*a, *b));
return 1;
static inline bool_t moduleVec4Check(jerry_value_t val, float_t *out) {
float_t *v = (float_t *)jerry_object_get_native_ptr(val, &VEC4_NATIVE_INFO);
if(!v) return false;
out[0] = v[0];
out[1] = v[1];
out[2] = v[2];
out[3] = v[3];
return true;
}
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
/**
* Returns the length (magnitude) of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (number).
* Creates the vec4 prototype with x/y/z/w (and u0/v0/u1/v1 alias)
* getter-setter properties and all methods, then registers the global
* vec4() constructor.
*/
static int moduleVec4Length(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleVec4(void) {
s_vec4Proto = jerry_object();
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
// Primary component names
jsDefineProperty(s_vec4Proto, "x", moduleVec4GetX, moduleVec4SetX);
jsDefineProperty(s_vec4Proto, "y", moduleVec4GetY, moduleVec4SetY);
jsDefineProperty(s_vec4Proto, "z", moduleVec4GetZ, moduleVec4SetZ);
jsDefineProperty(s_vec4Proto, "w", moduleVec4GetW, moduleVec4SetW);
lua_pushnumber(L, (lua_Number)glm_vec4_norm(*v));
return 1;
}
/**
* Returns the squared length of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (number).
*/
static int moduleVec4LengthSq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
lua_pushnumber(L, (lua_Number)glm_vec4_norm2(*v));
return 1;
}
/**
* Returns a normalized copy of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4Normalize(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_normalize_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* Linearly interpolates between two vec4 values.
*
* @param L Lua state. Arg 1: vec4 a. Arg 2: vec4 b. Arg 3: number t.
* @return 1 (new vec4).
*/
static int moduleVec4Lerp(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
float_t t = (float_t)luaL_checknumber(L, 3);
vec4 r;
glm_vec4_lerp(*a, *b, t, r);
moduleVec4Push(L, r);
return 1;
}
/**
* Returns a negated copy of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4Negate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_negate_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* Constructor: creates a vec4 from four optional numbers (defaults to 0).
*
* @param L Lua state. Args 1-4 (optional): x, y, z, w.
* @return 1 (new vec4).
*/
static int moduleVec4Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 v = {0, 0, 0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
if(top >= 3) v[2] = (float_t)luaL_checknumber(L, 3);
if(top >= 4) v[3] = (float_t)luaL_checknumber(L, 4);
moduleVec4Push(L, v);
return 1;
}
/**
* Registers the vec4 metatable and vec4 constructor global.
*
* @param L Lua state.
*/
static void moduleVec4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!luaL_newmetatable(L, "vec4_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec4Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec4NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec4OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec4OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec4OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec4OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec4OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec4OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec4OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec4Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec4Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec4LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec4Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec4Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec4Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec4", moduleVec4Create);
// UV alias names (same backing components)
jsDefineProperty(s_vec4Proto, "u0", moduleVec4GetU0, moduleVec4SetU0);
jsDefineProperty(s_vec4Proto, "v0", moduleVec4GetV0, moduleVec4SetV0);
jsDefineProperty(s_vec4Proto, "u1", moduleVec4GetU1, moduleVec4SetU1);
jsDefineProperty(s_vec4Proto, "v1", moduleVec4GetV1, moduleVec4SetV1);
jsDefineMethod(s_vec4Proto, "dot", moduleVec4Dot);
jsDefineMethod(s_vec4Proto, "length", moduleVec4Length);
jsDefineMethod(s_vec4Proto, "lengthSq", moduleVec4LengthSq);
jsDefineMethod(s_vec4Proto, "normalize", moduleVec4Normalize);
jsDefineMethod(s_vec4Proto, "negate", moduleVec4Negate);
jsDefineMethod(s_vec4Proto, "add", moduleVec4Add);
jsDefineMethod(s_vec4Proto, "sub", moduleVec4Sub);
jsDefineMethod(s_vec4Proto, "scale", moduleVec4Scale);
jsDefineMethod(s_vec4Proto, "lerp", moduleVec4Lerp);
jsRegister("vec4", moduleVec4Create);
}
+18 -20
View File
@@ -24,24 +24,22 @@
#include "script/module/display/moduletileset.h"
#include "script/module/scene/modulescene.h"
void moduleRegister(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
moduleScript(L);
moduleEntity(L);
moduleInput(L);
modulePlatform(L);
moduleLocale(L);
moduleTime(L);
moduleEvent(L);
moduleColor(L);
moduleSpriteBatch(L);
moduleMath(L);
moduleShader(L);
moduleUi(L);
moduleText(L);
moduleScreen(L);
moduleTexture(L);
moduleTileset(L);
moduleScene(L);
static void moduleRegister(void) {
moduleScript();
moduleEntity();
moduleInput();
modulePlatform();
moduleLocale();
moduleTime();
moduleEvent();
moduleColor();
moduleSpriteBatch();
moduleMath();
moduleShader();
moduleUi();
moduleText();
moduleScreen();
moduleTexture();
moduleTileset();
moduleScene();
}
+253
View File
@@ -10,3 +10,256 @@
#include "assert/assert.h"
#include "util/string.h"
#include "util/memory.h"
#include <stdlib.h>
/**
* Standard JerryScript external function signature.
* Usage: JS_FUNC(myFunction) { ... }
*/
#define JS_FUNC(name) \
static jerry_value_t name( \
const jerry_call_info_t *call_info_p, \
const jerry_value_t args_p[], \
const jerry_length_t args_count)
/**
* Return a type-error exception from a module function.
* Usage: return JS_THROW("message");
*/
#define JS_THROW(msg) jerry_throw_sz(JERRY_ERROR_TYPE, (msg))
/**
* Assert minimum argument count; return type error if not met.
*/
#define JS_REQUIRE_ARGS(n) do { \
if((jerry_length_t)(args_count) < (jerry_length_t)(n)) { \
return JS_THROW("Not enough arguments"); \
} \
} while(0)
/**
* Assert an argument is a number; return type error if not.
*/
#define JS_REQUIRE_NUMBER(i) do { \
if(!jerry_value_is_number(args_p[(i)])) { \
return JS_THROW("Expected number argument"); \
} \
} while(0)
/**
* Assert an argument is a string; return type error if not.
*/
#define JS_REQUIRE_STRING(i) do { \
if(!jerry_value_is_string(args_p[(i)])) { \
return JS_THROW("Expected string argument"); \
} \
} while(0)
/**
* Assert an argument is a function; return type error if not.
*/
#define JS_REQUIRE_FUNCTION(i) do { \
if(!jerry_value_is_function(args_p[(i)])) { \
return JS_THROW("Expected function argument"); \
} \
} while(0)
/**
* Assert an argument is an object; return type error if not.
*/
#define JS_REQUIRE_OBJECT(i) do { \
if(!jerry_value_is_object(args_p[(i)])) { \
return JS_THROW("Expected object argument"); \
} \
} while(0)
/* JS_PTR_NATIVE_INFO is declared in scriptcontext.h and defined in
scriptcontext.c so all TUs share a single address for native-ptr lookups. */
/**
* Register a global JS function.
*
* @param name Function name as seen in scripts.
* @param fn C handler function.
*/
static inline void jsRegister(
const char_t *name,
jerry_external_handler_t fn
) {
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);
}
/**
* Set a global numeric constant.
*/
static inline void jsSetNumber(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 jsSetInt(const char_t *name, int32_t value) {
jsSetNumber(name, (double)value);
}
/**
* Set a global string constant.
*/
static inline void jsSetString(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);
}
/**
* Set a global JS value. Caller retains ownership of the value and must free
* it independently.
*/
static inline void jsSetValue(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 jsWrapPointer(void *ptr) {
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &JS_PTR_NATIVE_INFO, ptr);
return obj;
}
/**
* Unwrap a C pointer from a JS object created by jsWrapPointer.
* Returns NULL if the object does not carry a matching native pointer.
*/
static inline void *jsUnwrapPointer(jerry_value_t val) {
if(!jerry_value_is_object(val)) return NULL;
return jerry_object_get_native_ptr(val, &JS_PTR_NATIVE_INFO);
}
/**
* Evaluate a JS source string in the global scope.
* Errors are silently discarded; use scriptContextExec for error-propagating
* execution.
*/
static inline void jsEvalStr(const char_t *script) {
jerry_value_t result = jerry_eval(
(const jerry_char_t *)script,
strlen(script),
JERRY_PARSE_NO_OPTS
);
jerry_value_free(result);
}
/**
* 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 jsToString(
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.
* Both getter and setter are external C functions.
*
* @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 jsDefineProperty(
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 jsDefineMethod(
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 jsExceptionMessage(
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);
}
+5 -8
View File
@@ -6,22 +6,19 @@
*/
#pragma once
#include "script/scriptcontext.h"
#include "assert/assert.h"
#include "script/module/modulebase.h"
#include "script/module/moduleplatformplatform.h"
#ifndef DUSK_TARGET_SYSTEM
#error "DUSK_TARGET_SYSTEM must be defined"
#endif
#define MODULE_PLATFORM_VALUE "PLATFORM = '" DUSK_TARGET_SYSTEM "'\n"
#define MODULE_PLATFORM_VALUE "var PLATFORM = '" DUSK_TARGET_SYSTEM "';\n"
static void modulePlatform(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
luaL_dostring(L, MODULE_PLATFORM_VALUE);
static void modulePlatform(void) {
jsEvalStr(MODULE_PLATFORM_VALUE);
#ifdef modulePlatformPlatform
modulePlatformPlatform(L);
modulePlatformPlatform();
#endif
}
+42 -92
View File
@@ -9,118 +9,68 @@
#include "script/module/modulebase.h"
#include "scene/scene.h"
/**
* __index metamethod for the Scene table. Handles dynamic read-only properties.
*
* @param L Lua state. Arg 1: table, Arg 2: key.
* @return 1.
*/
static int moduleSceneIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
JS_FUNC(moduleSceneSet) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_STRING(0);
const char_t *key = lua_tostring(L, 2);
assertNotNull(key, "Scene property key cannot be NULL");
char_t name[ASSET_FILE_PATH_MAX];
jsToString(args_p[0], name, sizeof(name));
if(name[0] == '\0') return JS_THROW("Scene.set: Scene name cannot be empty");
// if(stringEquals(key, "current")) {
// if(SCENE.sceneActive) {
// lua_pushstring(L, SCENE.sceneCurrent);
// } else {
// lua_pushnil(L);
// }
// return 1;
// }
lua_pushnil(L);
return 1;
sceneSet(name);
return jerry_undefined();
}
/**
* Attached Scene.set method to invoke internal C method.
*
* @param L Lua state.
* @return Number of return values on the Lua stack.
*/
static int moduleSceneSet(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) {
luaL_error(L, "Scene.set requires a string argument");
return 0;
JS_FUNC(moduleSceneGetCurrent) {
if(SCENE.sceneCurrent[0] == '\0') return jerry_undefined();
return jerry_string_sz(SCENE.sceneCurrent);
}
sceneSet(lua_tostring(L, 1));
return 0;
static void moduleSceneReset(void) {
if(SCENE.scriptRef != SCENE_SCRIPT_REF_NONE) {
jerry_value_free(SCENE.scriptRef);
SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
}
/**
* Resets the scene back to a clean slate, this is called before loading a new
* scene to ensure that no old state bleeds through.
*/
static void moduleSceneReset(lua_State *L) {
if(SCENE.scriptRef != LUA_NOREF) {
luaL_unref(L, LUA_REGISTRYINDEX, SCENE.scriptRef);
SCENE.scriptRef = LUA_NOREF;
jerry_value_t obj = jerry_object();
jsDefineMethod(obj, "set", moduleSceneSet);
jsDefineProperty(obj, "current", moduleSceneGetCurrent, NULL);
jsSetValue("Scene", obj);
jerry_value_free(obj);
}
lua_newtable(L);
// Scene.set
lua_pushcfunction(L, moduleSceneSet);
lua_setfield(L, -2, "set");
// Metatable for dynamic read-only properties (e.g. Scene.current)
lua_newtable(L);
lua_pushcfunction(L, moduleSceneIndex);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_setglobal(L, "Scene");
}
/**
* Invokes a method on the current scene.
*
* @param method Which method to call.
* @return Any error state that happened.
*/
static errorret_t moduleSceneCall(lua_State *L, const char_t *method) {
assertNotNull(L, "Lua state cannot be NULL");
static errorret_t moduleSceneCall(const char_t *method) {
assertStrLenMin(method, 1, "Method name cannot be empty");
assertTrue(
SCENE.scriptRef != LUA_NOREF && SCENE.scriptRef != LUA_REFNIL,
"No active scene script to call method on"
);
// Get the scene table
lua_rawgeti(L, LUA_REGISTRYINDEX, SCENE.scriptRef);
if(!lua_istable(L, -1)) {
lua_pop(L, 1);
errorThrow("Scene script ref %d is not a table", SCENE.scriptRef);
if(SCENE.scriptRef == SCENE_SCRIPT_REF_NONE) {
errorThrow("No active scene script to call method on");
}
// Get the method from the scene table
lua_getfield(L, -1, method);
if(!lua_isfunction(L, -1)) {
lua_pop(L, 2);
errorThrow("Scene method '%s' not found", method);// TODO: Needed?
jerry_value_t key = jerry_string_sz(method);
jerry_value_t fn = jerry_object_get(SCENE.scriptRef, key);
jerry_value_free(key);
if(!jerry_value_is_function(fn)) {
jerry_value_free(fn);
errorThrow("Scene method '%s' not found", method);
}
// Push the scene table as the first argument (self)
lua_pushvalue(L, -2);
jerry_value_t result = jerry_call(fn, SCENE.scriptRef, NULL, 0);
jerry_value_free(fn);
// Call the method with 1 argument (the scene table) and 0 return values
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
const char_t *err = lua_tostring(L, -1);
lua_pop(L, 2);// Pops the error message and the scene table
errorThrow("Scene:%s failed: %s", method, err);
if(jerry_value_is_exception(result)) {
char_t errMsg[512];
jsExceptionMessage(result, errMsg, sizeof(errMsg));
jerry_value_free(result);
errorThrow("Scene:%s failed: %s", method, errMsg);
}
lua_pop(L, 1);// Pops the scene table
jerry_value_free(result);
errorOk();
}
static void moduleScene(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
moduleSceneReset(L);
static void moduleScene(void) {
moduleSceneReset();
}
+40 -46
View File
@@ -9,74 +9,68 @@
#include "script/module/modulebase.h"
#include "console/console.h"
static int moduleScriptPrint(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
JS_FUNC(moduleScriptPrint) {
char_t buf[512];
char_t msg[4096];
size_t msgLen = 0;
int n = lua_gettop(L);
luaL_Buffer b;
luaL_buffinit(L, &b);
for(jerry_length_t i = 0; i < args_count; ++i) {
jerry_value_t strVal = jerry_value_to_string(args_p[i]);
jsToString(strVal, buf, sizeof(buf));
jerry_value_free(strVal);
for(int i = 1; i <= n; ++i) {
size_t len;
const char *s = luaL_tolstring(L, i, &len);
luaL_addlstring(&b, s, len);
lua_pop(L, 1);
if(i < n) luaL_addlstring(&b, "\t", 1);
size_t partLen = strlen(buf);
if(msgLen + partLen + 1 < sizeof(msg)) {
stringCopy(msg + msgLen, buf, sizeof(msg) - msgLen);
msgLen += partLen;
}
if(i + 1 < args_count && msgLen + 1 < sizeof(msg)) {
msg[msgLen++] = '\t';
msg[msgLen] = '\0';
}
}
luaL_pushresult(&b);
const char *msg = lua_tostring(L, -1);
consolePrint("%s", msg);
return 0;
return jerry_undefined();
}
static int moduleScriptInclude(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) {
luaL_error(L, "Expected string filename");
return 0;
JS_FUNC(moduleScriptInclude) {
if(args_count < 1 || !jerry_value_is_string(args_p[0])) {
return JS_THROW("Expected string filename");
}
scriptcontext_t* ctx = *(scriptcontext_t**)lua_getextraspace(L);
if(ctx == NULL) {
luaL_error(L, "Script context is NULL");
return 0;
}
char_t filename[1024];
jsToString(args_p[0], filename, sizeof(filename));
const char_t *filename = luaL_checkstring(L, 1);
if(filename == NULL || filename[0] == '\0') {
luaL_error(L, "Filename cannot be NULL");
return 0;
if(filename[0] == '\0') {
return JS_THROW("Filename cannot be empty");
}
char_t buffer[1024];
stringCopy(buffer, filename, 1024);
stringCopy(buffer, filename, sizeof(buffer));
size_t len = strlen(buffer);
if(len < 4 || stringCompare(&buffer[len - 4], ".lua") != 0) {
if(len + 4 >= 1024) {
luaL_error(L, "Filename too long to append .lua");
return 0;
if(len < 3 || stringCompare(&buffer[len - 3], ".js") != 0) {
if(len + 3 >= sizeof(buffer)) {
return JS_THROW("Filename too long to append .js");
}
stringCopy(&buffer[len], ".lua", 5);
stringCopy(&buffer[len], ".js", 4);
}
int32_t stackBase = lua_gettop(L);
errorret_t err = scriptContextExecFile(ctx, buffer);
jerry_value_t result = 0;
errorret_t err = scriptContextExecFile(scriptContextCurrent, buffer, &result);
if(err.code != ERROR_OK) {
luaL_error(L, "Failed to include script file: %s", buffer);
if(result != 0) jerry_value_free(result);
errorCatch(errorPrint(err));
return 0;
return JS_THROW("Failed to include script file");
}
return lua_gettop(L) - stackBase;
if(result == 0) return jerry_undefined();
return result;
}
static void moduleScript(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_register(L, "print", moduleScriptPrint);
lua_register(L, "include", moduleScriptInclude);
static void moduleScript(void) {
jsRegister("print", moduleScriptPrint);
jsRegister("include", moduleScriptInclude);
}
+10 -25
View File
@@ -9,33 +9,18 @@
#include "script/module/modulebase.h"
#include "time/time.h"
static int moduleTimeIndex(lua_State *L) {
const char_t *key = lua_tostring(L, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
if(stringCompare(key, "delta") == 0) {
lua_pushnumber(L, TIME.delta);
return 1;
} else if(stringCompare(key, "time") == 0) {
lua_pushnumber(L, TIME.time);
return 1;
JS_FUNC(moduleTimeGetDelta) {
return jerry_number(TIME.delta);
}
lua_pushnil(L);
return 1;
JS_FUNC(moduleTimeGetTime) {
return jerry_number(TIME.time);
}
static void moduleTime(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(luaL_newmetatable(L, "time_mt")) {
lua_pushcfunction(L, moduleTimeIndex);
lua_setfield(L, -2, "__index");
}
lua_pop(L, 1);
dusktime_t **ud = (dusktime_t**)lua_newuserdata(L, sizeof(dusktime_t*));
*ud = &TIME;
luaL_setmetatable(L, "time_mt");
lua_setglobal(L, "TIME");
static void moduleTime(void) {
jerry_value_t obj = jerry_object();
jsDefineProperty(obj, "delta", moduleTimeGetDelta, NULL);
jsDefineProperty(obj, "time", moduleTimeGetTime, NULL);
jsSetValue("TIME", obj);
jerry_value_free(obj);
}
+1 -2
View File
@@ -8,6 +8,5 @@
#pragma once
#include "script/module/modulebase.h"
static void moduleUi(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleUi(void) {
}
+39 -24
View File
@@ -17,27 +17,26 @@
#include "script/scriptgame.h"
#endif
scriptcontext_t *scriptContextCurrent = NULL;
const jerry_object_native_info_t JS_PTR_NATIVE_INFO = {
.free_cb = NULL,
.number_of_references = 0,
.offset_of_references = 0
};
errorret_t scriptContextInit(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL");
memoryZero(context, sizeof(scriptcontext_t));
// Create a new Lua state for this context.
context->luaState = luaL_newstate();
if(context->luaState == NULL) {
errorThrow("Failed to init Lua state");
}
luaL_openlibs(context->luaState);
jerry_init(JERRY_INIT_EMPTY);
scriptContextCurrent = context;
// Store context in Lua extraspace
*(scriptcontext_t**)lua_getextraspace(context->luaState) = context;
moduleRegister();
// Register built-in script modules.
moduleRegister(context->luaState);
// Fire any game script init function if defined.
#ifdef SCRIPT_GAME_INIT
SCRIPT_GAME_INIT(L);
SCRIPT_GAME_INIT();
#endif
errorOk();
@@ -47,24 +46,42 @@ errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script) {
assertNotNull(context, "Script context cannot be NULL");
assertNotNull(script, "Script cannot be NULL");
if(luaL_dostring(context->luaState, script) != LUA_OK) {
const char_t *strErr = lua_tostring(context->luaState, -1);
lua_pop(context->luaState, 1);
errorThrow("Failed to execute Lua: %s", strErr);
jerry_value_t result = jerry_eval(
(const jerry_char_t *)script,
strlen(script),
JERRY_PARSE_NO_OPTS
);
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("Failed to execute script: %s", buf);
}
jerry_value_free(result);
errorOk();
}
errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname) {
errorret_t scriptContextExecFile(
scriptcontext_t *ctx,
const char_t *fname,
jerry_value_t *resultOut
) {
assertNotNull(ctx, "Script context cannot be NULL");
assertNotNull(fname, "Filename cannot be NULL");
return assetScriptLoad(fname, ctx);
return assetScriptLoad(fname, ctx, resultOut);
}
void scriptContextDispose(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL");
assertNotNull(context->luaState, "Lua state is not initialized");
for(uint8_t i = 0; i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS; i++) {
event_t *event = context->subscribedEvents[i];
@@ -72,8 +89,6 @@ void scriptContextDispose(scriptcontext_t *context) {
eventUnsubscribeScriptContext(event, context);
}
if(context->luaState != NULL) {
lua_close(context->luaState);
context->luaState = NULL;
}
jerry_cleanup();
scriptContextCurrent = NULL;
}
+21 -7
View File
@@ -8,19 +8,26 @@
#pragma once
#include "error/error.h"
#include "scriptvalue.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <jerryscript.h>
typedef struct event_s event_t;
#define SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS 64
typedef struct scriptcontext_s {
lua_State *luaState;
event_t* subscribedEvents[SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS];
} scriptcontext_t;
/** Points to the currently active script context. Set by scriptContextInit. */
extern scriptcontext_t *scriptContextCurrent;
/**
* Singleton native-info tag for engine-owned C pointers wrapped in JS objects.
* A single global instance ensures jerry_object_get_native_ptr() matches across
* all compilation units (including event.c and module headers).
*/
extern const jerry_object_native_info_t JS_PTR_NATIVE_INFO;
/**
* Initialize a script context.
*
@@ -30,10 +37,10 @@ typedef struct scriptcontext_s {
errorret_t scriptContextInit(scriptcontext_t *context);
/**
* Execute a script within a script context.
* Execute a script string within a script context.
*
* @param context The script context to use.
* @param script The script to execute.
* @param script The JS source to execute.
* @return The error return value.
*/
errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script);
@@ -43,9 +50,16 @@ errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script);
*
* @param ctx The script context to use.
* @param fname The filename of the script to execute.
* @param result Optional out-parameter for the script's return value.
* Caller must call jerry_value_free() on it when done.
* Pass NULL to discard the return value.
* @return The error return value.
*/
errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname);
errorret_t scriptContextExecFile(
scriptcontext_t *ctx,
const char_t *fname,
jerry_value_t *result
);
/**
* Dispose of a script context.
@@ -6,8 +6,8 @@
*/
#pragma once
#include "script/scriptcontext.h"
#include "script/module/modulebase.h"
static void modulePlatformDolphin(lua_State *L) {
luaL_dostring(L, "DOLPHIN = true\n");
static void modulePlatformDolphin(void) {
jsEvalStr("var DOLPHIN = true;\n");
}
@@ -6,8 +6,8 @@
*/
#pragma once
#include "script/scriptcontext.h"
#include "script/module/modulebase.h"
static void modulePlatformLinux(lua_State *L) {
luaL_dostring(L, "LINUX = true\n");
static void modulePlatformLinux(void) {
jsEvalStr("var LINUX = true;\n");
}
@@ -6,8 +6,8 @@
*/
#pragma once
#include "script/scriptcontext.h"
#include "script/module/modulebase.h"
void modulePlatformPSP(lua_State *L) {
luaL_dostring(L, "PSP = true\n");
static void modulePlatformPSP(void) {
jsEvalStr("var PSP = true;\n");
}
+92 -133
View File
@@ -6,236 +6,195 @@
*/
#pragma once
#include "script/scriptcontext.h"
#include "script/module/modulebase.h"
#include "item/inventory.h"
#include "item/backpack.h"
#include "assert/assert.h"
static int moduleInventoryItemExists(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryItemExists: Expected inventory pointer as first argument");
return 0;
JS_FUNC(moduleInventoryItemExists) {
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryItemExists: Expected inventory pointer as first argument");
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inventoryItemExists: Expected item ID as second argument");
return 0;
if(!jerry_value_is_number(args_p[1])) {
return JS_THROW("inventoryItemExists: Expected item ID as second argument");
}
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1);
itemid_t item = (itemid_t)lua_tonumber(L, 2);
inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL.");
if(item == ITEM_ID_NULL) {
luaL_error(L, "inventoryItemExists: Item ID cannot be ITEM_ID_NULL");
return 0;
return JS_THROW("inventoryItemExists: Item ID cannot be ITEM_ID_NULL");
}
bool_t hasItem = inventoryItemExists(inventory, item);
lua_pushboolean(L, hasItem);
return 1;
return jerry_boolean(hasItem);
}
static int moduleInventorySet(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventorySet: Expected inventory pointer as first argument");
return 0;
JS_FUNC(moduleInventorySet) {
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventorySet: Expected inventory pointer as first argument");
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inventorySet: Expected item ID as second argument");
return 0;
if(!jerry_value_is_number(args_p[1])) {
return JS_THROW("inventorySet: Expected item ID as second argument");
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "inventorySet: Expected quantity as third argument");
return 0;
if(!jerry_value_is_number(args_p[2])) {
return JS_THROW("inventorySet: Expected quantity as third argument");
}
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1);
itemid_t item = (itemid_t)lua_tonumber(L, 2);
uint8_t quantity = (uint8_t)lua_tonumber(L, 3);
inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
uint8_t quantity = (uint8_t)jerry_value_as_number(args_p[2]);
assertNotNull(inventory, "Inventory pointer cannot be NULL.");
inventorySet(inventory, item, quantity);
return 0;
return jerry_undefined();
}
static int moduleInventoryAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryAdd: Expected inventory pointer as first argument");
return 0;
JS_FUNC(moduleInventoryAdd) {
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryAdd: Expected inventory pointer as first argument");
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inventoryAdd: Expected item ID as second argument");
return 0;
if(!jerry_value_is_number(args_p[1])) {
return JS_THROW("inventoryAdd: Expected item ID as second argument");
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "inventoryAdd: Expected quantity as third argument");
return 0;
if(!jerry_value_is_number(args_p[2])) {
return JS_THROW("inventoryAdd: Expected quantity as third argument");
}
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1);
itemid_t item = (itemid_t)lua_tonumber(L, 2);
uint8_t quantity = (uint8_t)lua_tonumber(L, 3);
inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
uint8_t quantity = (uint8_t)jerry_value_as_number(args_p[2]);
assertNotNull(inventory, "Inventory pointer cannot be NULL.");
inventoryAdd(inventory, item, quantity);
return 0;
return jerry_undefined();
}
static int moduleInventoryRemove(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryRemove: Expected inventory pointer as first argument");
return 0;
JS_FUNC(moduleInventoryRemove) {
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryRemove: Expected inventory pointer as first argument");
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inventoryRemove: Expected item ID as second argument");
return 0;
if(!jerry_value_is_number(args_p[1])) {
return JS_THROW("inventoryRemove: Expected item ID as second argument");
}
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1);
itemid_t item = (itemid_t)lua_tonumber(L, 2);
inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL.");
if(lua_gettop(L) >= 3) {
if(!lua_isnumber(L, 3)) {
luaL_error(L, "inventoryRemove: Expected quantity as third argument");
return 0;
if(args_count >= 3) {
if(!jerry_value_is_number(args_p[2])) {
return JS_THROW("inventoryRemove: Expected quantity as third argument");
}
uint8_t amount = (uint8_t)lua_tonumber(L, 3);
uint8_t amount = (uint8_t)jerry_value_as_number(args_p[2]);
uint8_t currentQuantity = inventoryGetCount(inventory, item);
if(amount >= currentQuantity) {
inventoryRemove(inventory, item);
return 0;
return jerry_undefined();
}
inventorySet(inventory, item, currentQuantity - amount);
return 0;
return jerry_undefined();
}
inventoryRemove(inventory, item);
return 0;
return jerry_undefined();
}
static int moduleInventoryGetCount(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryGetCount: Expected inventory pointer as first argument");
return 0;
JS_FUNC(moduleInventoryGetCount) {
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryGetCount: Expected inventory pointer as first argument");
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inventoryGetCount: Expected item ID as second argument");
return 0;
if(!jerry_value_is_number(args_p[1])) {
return JS_THROW("inventoryGetCount: Expected item ID as second argument");
}
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1);
itemid_t item = (itemid_t)lua_tonumber(L, 2);
inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL.");
uint8_t count = inventoryGetCount(inventory, item);
lua_pushnumber(L, count);
return 1;
return jerry_number(count);
}
static int moduleInventoryIsFull(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryIsFull: Expected inventory pointer as first argument");
return 0;
JS_FUNC(moduleInventoryIsFull) {
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryIsFull: Expected inventory pointer as first argument");
}
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1);
inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
assertNotNull(inventory, "Inventory pointer cannot be NULL.");
bool_t isFull = inventoryIsFull(inventory);
lua_pushboolean(L, isFull);
return 1;
return jerry_boolean(isFull);
}
static int moduleInventoryItemFull(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryItemFull: Expected inventory pointer as first argument");
return 0;
JS_FUNC(moduleInventoryItemFull) {
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryItemFull: Expected inventory pointer as first argument");
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inventoryItemFull: Expected item ID as second argument");
return 0;
if(!jerry_value_is_number(args_p[1])) {
return JS_THROW("inventoryItemFull: Expected item ID as second argument");
}
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1);
itemid_t item = (itemid_t)lua_tonumber(L, 2);
inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL.");
bool_t isFull = inventoryItemFull(inventory, item);
lua_pushboolean(L, isFull);
return 1;
return jerry_boolean(isFull);
}
static int moduleInventorySort(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventorySort: Expected inventory pointer as first argument");
return 0;
JS_FUNC(moduleInventorySort) {
if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventorySort: Expected inventory pointer as first argument");
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inventorySort: Expected sort type as second argument");
return 0;
if(!jerry_value_is_number(args_p[1])) {
return JS_THROW("inventorySort: Expected sort type as second argument");
}
bool_t reverse = false;
if(lua_gettop(L) >= 3) {
if(!lua_isboolean(L, 3)) {
luaL_error(L, "inventorySort: Expected reverse flag as third argument");
return 0;
if(args_count >= 3) {
if(!jerry_value_is_boolean(args_p[2])) {
return JS_THROW("inventorySort: Expected reverse flag as third argument");
}
reverse = (bool_t)lua_toboolean(L, 3);
reverse = (bool_t)jerry_value_is_true(args_p[2]);
}
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1);
inventorysort_t sortBy = (inventorysort_t)lua_tonumber(L, 2);
inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
inventorysort_t sortBy = (inventorysort_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL.");
inventorySort(inventory, sortBy, reverse);
return 0;
return jerry_undefined();
}
static void moduleItem(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
static void moduleItem(void) {
jsEvalStr(ITEM_SCRIPT);
luaL_dostring(L, ITEM_SCRIPT);
jerry_value_t backpack = jsWrapPointer(&BACKPACK);
jsSetValue("BACKPACK", backpack);
jerry_value_free(backpack);
lua_pushlightuserdata(L, &BACKPACK);
lua_setglobal(L, "BACKPACK");
lua_register(L, "inventoryItemExists", moduleInventoryItemExists);
lua_register(L, "inventoryAdd", moduleInventoryAdd);
lua_register(L, "inventorySet", moduleInventorySet);
lua_register(L, "inventoryRemove", moduleInventoryRemove);
lua_register(L, "inventoryGetCount", moduleInventoryGetCount);
lua_register(L, "inventoryIsFull", moduleInventoryIsFull);
lua_register(L, "inventoryItemFull", moduleInventoryItemFull);
lua_register(L, "inventorySort", moduleInventorySort);
jsRegister("inventoryItemExists", moduleInventoryItemExists);
jsRegister("inventoryAdd", moduleInventoryAdd);
jsRegister("inventorySet", moduleInventorySet);
jsRegister("inventoryRemove", moduleInventoryRemove);
jsRegister("inventoryGetCount", moduleInventoryGetCount);
jsRegister("inventoryIsFull", moduleInventoryIsFull);
jsRegister("inventoryItemFull", moduleInventoryItemFull);
jsRegister("inventorySort", moduleInventorySort);
}
@@ -6,96 +6,58 @@
*/
#pragma once
#include "script/scriptcontext.h"
#include "script/module/modulebase.h"
#include "assert/assert.h"
#include "story/storyflag.h"
static int moduleStoryFlagGet(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "Expected flag ID.");
return 0;
}
storyflag_t flag = (storyflag_t)lua_tonumber(L, 1);
JS_FUNC(moduleStoryFlagGet) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
storyflag_t flag = (storyflag_t)jerry_value_as_number(args_p[0]);
if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) {
luaL_error(L, "Invalid flag ID %d", flag);
return 0;
return JS_THROW("storyFlagGet: invalid flag ID");
}
return jerry_number((double)storyFlagGet(flag));
}
storyflagvalue_t value = storyFlagGet(flag);
lua_pushnumber(L, value);
return 1;
}
static int moduleStoryFlagSet(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "Expected flag ID.");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "Expected flag value.");
return 0;
}
storyflag_t flag = (storyflag_t)lua_tonumber(L, 1);
JS_FUNC(moduleStoryFlagSet) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
storyflag_t flag = (storyflag_t)jerry_value_as_number(args_p[0]);
if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) {
luaL_error(L, "Invalid flag ID %d", flag);
return 0;
return JS_THROW("storyFlagSet: invalid flag ID");
}
storyflagvalue_t value = (storyflagvalue_t)lua_tonumber(L, 2);
storyflagvalue_t value = (storyflagvalue_t)jerry_value_as_number(args_p[1]);
storyFlagSet(flag, value);
return 0;
return jerry_undefined();
}
static int moduleStoryFlagIncrement(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "Expected flag ID.");
return 0;
}
storyflag_t flag = (storyflag_t)lua_tonumber(L, 1);
JS_FUNC(moduleStoryFlagIncrement) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
storyflag_t flag = (storyflag_t)jerry_value_as_number(args_p[0]);
if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) {
luaL_error(L, "Invalid flag ID %d", flag);
return 0;
return JS_THROW("storyFlagIncrement: invalid flag ID");
}
storyFlagSet(flag, storyFlagGet(flag) + 1);
return jerry_undefined();
}
storyflagvalue_t value = storyFlagGet(flag);
storyFlagSet(flag, value + 1);
return 0;
}
static int moduleStoryFlagDecrement(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "Expected flag ID.");
return 0;
}
storyflag_t flag = (storyflag_t)lua_tonumber(L, 1);
JS_FUNC(moduleStoryFlagDecrement) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
storyflag_t flag = (storyflag_t)jerry_value_as_number(args_p[0]);
if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) {
luaL_error(L, "Invalid flag ID %d", flag);
return 0;
return JS_THROW("storyFlagDecrement: invalid flag ID");
}
storyFlagSet(flag, storyFlagGet(flag) - 1);
return jerry_undefined();
}
storyflagvalue_t value = storyFlagGet(flag);
storyFlagSet(flag, value - 1);
return 0;
}
static void moduleStoryFlag(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_register(L, "storyFlagGet", moduleStoryFlagGet);
lua_register(L, "storyFlagSet", moduleStoryFlagSet);
lua_register(L, "storyFlagIncrement", moduleStoryFlagIncrement);
lua_register(L, "storyFlagDecrement", moduleStoryFlagDecrement);
static void moduleStoryFlag(void) {
jsRegister("storyFlagGet", moduleStoryFlagGet);
jsRegister("storyFlagSet", moduleStoryFlagSet);
jsRegister("storyFlagIncrement", moduleStoryFlagIncrement);
jsRegister("storyFlagDecrement", moduleStoryFlagDecrement);
}
+7 -7
View File
@@ -45,7 +45,7 @@ out = [
"",
]
lua = []
js = []
for name, (r, g, b, a) in colors.items():
r8, g8, b8, a8 = (int(float(ch) * 255) for ch in (r, g, b, a))
macro = "COLOR_" + name.upper()
@@ -60,16 +60,16 @@ for name, (r, g, b, a) in colors.items():
f"#define {macro} {macro}_4B",
"",
]
lua += [
f"function color{camel}()",
f" return color({r8}, {g8}, {b8}, {a8})",
"end",
js += [
f"function color{camel}() {{",
f" return color({r8}, {g8}, {b8}, {a8});",
"}",
"",
]
out.append("// Lua color functions")
out.append("// JS color functions")
out.append("#define COLOR_SCRIPT \\")
for line in "\n".join(lua).rstrip().splitlines():
for line in "\n".join(js).rstrip().splitlines():
out.append(f' "{line}\\n" \\')
out[-1] = out[-1].rstrip(" \\")
out.append("")
+1 -1
View File
@@ -50,7 +50,7 @@ out += [
"static const char_t *INPUT_ACTION_SCRIPT =",
]
for input_id in input_ids:
out.append(f" \"{id_enum(input_id)} = {id_values[input_id]}\\n\"")
out.append(f" \"var {id_enum(input_id)} = {id_values[input_id]};\\n\"")
out += [";", ""]
os.makedirs(os.path.dirname(args.output), exist_ok=True)