diff --git a/assets/entities/cube.lua b/assets/entities/cube.lua new file mode 100644 index 00000000..a116eb2e --- /dev/null +++ b/assets/entities/cube.lua @@ -0,0 +1,25 @@ +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 diff --git a/assets/init.lua b/assets/init.lua index 71748497..34303093 100644 --- a/assets/init.lua +++ b/assets/init.lua @@ -1,8 +1,3 @@ -module('input') -module('platform') -module('scene') -module('locale') - -- Default Input bindings. if PSP then inputBind("up", INPUT_ACTION_UP) @@ -77,5 +72,4 @@ else print("Unknown platform, no default input bindings set.") end --- Hand off to initial scene. -sceneSet('test/scene.lua') \ No newline at end of file +Scene.set('scenes/cube.lua') \ No newline at end of file diff --git a/assets/scenes/cube.lua b/assets/scenes/cube.lua new file mode 100644 index 00000000..7b4850d8 --- /dev/null +++ b/assets/scenes/cube.lua @@ -0,0 +1,30 @@ +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 diff --git a/cmake/targets/dolphin.cmake b/cmake/targets/dolphin.cmake index f42f9040..e4f8c58f 100644 --- a/cmake/targets/dolphin.cmake +++ b/cmake/targets/dolphin.cmake @@ -39,7 +39,7 @@ set(LUA_C_FILES 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) +target_compile_definitions(liblua PUBLIC LUA_USE_C89) add_library(lua::lua ALIAS liblua) set(Lua_FOUND TRUE CACHE BOOL "Lua found" FORCE) diff --git a/src/dusk/asset/loader/script/assetscriptloader.c b/src/dusk/asset/loader/script/assetscriptloader.c index ba99edd1..8ddee48c 100644 --- a/src/dusk/asset/loader/script/assetscriptloader.c +++ b/src/dusk/asset/loader/script/assetscriptloader.c @@ -19,13 +19,13 @@ errorret_t assetScriptLoader(assetfile_t *file) { errorChain(assetFileOpen(file)); // Request loading - if(!lua_load( + if(lua_load( script->ctx->luaState, assetScriptReader, file, file->filename, NULL - ) == LUA_OK) { + ) != 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); diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index 8c3b772e..dc2b2613 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -47,13 +47,10 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { physicsManagerInit(); errorChain(networkInit()); errorChain(gameInit()); - consolePrint("Engine initialized"); - + /* Run the init script. */ - scriptcontext_t ctx; - errorChain(scriptContextInit(&ctx)); - errorChain(scriptContextExecFile(&ctx, "init.lua")); - scriptContextDispose(&ctx); + consolePrint("Engine initialized"); + errorChain(scriptContextExecFile(&SCRIPT_MANAGER.mainContext, "init.lua")); errorOk(); } diff --git a/src/dusk/entity/component/display/entitycamera.c b/src/dusk/entity/component/display/entitycamera.c index fd1b238a..ae3d1c53 100644 --- a/src/dusk/entity/component/display/entitycamera.c +++ b/src/dusk/entity/component/display/entitycamera.c @@ -6,6 +6,8 @@ */ #include "entity/entitymanager.h" +#include "entity/entity.h" +#include "entity/component/display/entityposition.h" #include "display/framebuffer/framebuffer.h" #include "display/screen/screen.h" @@ -92,4 +94,38 @@ void entityCameraGetProjection( out ); } +} + +entityid_t entityCameraGetCurrent(void) { + entityid_t camEnts[ENTITY_COUNT_MAX]; + componentid_t camComps[ENTITY_COUNT_MAX]; + entityid_t count = componentGetEntitiesWithComponent( + COMPONENT_TYPE_CAMERA, camEnts, camComps + ); + if(count == 0) return ENTITY_COUNT_MAX; + return camEnts[0]; +} + +void entityCameraGetForward(const entityid_t entityId, vec2 out) { + componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION); + entityposition_t *pos = entityPositionGet(entityId, posComp); + // View matrix column layout: M[col][row], forward = {-M[0][2], -M[1][2], -M[2][2]} + float_t fx = -pos->transform[0][2]; + float_t fz = -pos->transform[2][2]; + float_t len = sqrtf(fx * fx + fz * fz); + if(len > 1e-6f) { fx /= len; fz /= len; } + out[0] = fx; + out[1] = fz; +} + +void entityCameraGetRight(const entityid_t entityId, vec2 out) { + componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION); + entityposition_t *pos = entityPositionGet(entityId, posComp); + // View matrix column layout: right = {M[0][0], M[1][0], M[2][0]} + float_t rx = pos->transform[0][0]; + float_t rz = pos->transform[2][0]; + float_t len = sqrtf(rx * rx + rz * rz); + if(len > 1e-6f) { rx /= len; rz /= len; } + out[0] = rx; + out[1] = rz; } \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitycamera.h b/src/dusk/entity/component/display/entitycamera.h index a258262e..59b6fa46 100644 --- a/src/dusk/entity/component/display/entitycamera.h +++ b/src/dusk/entity/component/display/entitycamera.h @@ -96,4 +96,27 @@ void entityCameraSetZFar( const entityid_t ent, const componentid_t comp, const float_t zFar -); \ No newline at end of file +); + +/** + * Returns the entity ID of the first active camera, or ENTITY_COUNT_MAX if none. + */ +entityid_t entityCameraGetCurrent(void); + +/** + * Gets the camera's horizontal forward direction (XZ plane) from its position + * component. Automatically finds the position component on the entity. + * + * @param entityId The camera entity ID. + * @param out Output vec2: {forwardX, forwardZ} normalized. + */ +void entityCameraGetForward(const entityid_t entityId, vec2 out); + +/** + * Gets the camera's horizontal right direction (XZ plane) from its position + * component. Automatically finds the position component on the entity. + * + * @param entityId The camera entity ID. + * @param out Output vec2: {rightX, rightZ} normalized. + */ +void entityCameraGetRight(const entityid_t entityId, vec2 out); \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitymaterial.c b/src/dusk/entity/component/display/entitymaterial.c index 5aa7d9a0..da65d78d 100644 --- a/src/dusk/entity/component/display/entitymaterial.c +++ b/src/dusk/entity/component/display/entitymaterial.c @@ -48,4 +48,15 @@ void entityMaterialSetShader( entityId, componentId, COMPONENT_TYPE_MATERIAL ); mat->shader = shader; +} + +void entityMaterialSetColor( + const entityid_t entityId, + const componentid_t componentId, + const color_t color +) { + entitymaterial_t *mat = componentGetData( + entityId, componentId, COMPONENT_TYPE_MATERIAL + ); + mat->material.unlit.color = color; } \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitymaterial.h b/src/dusk/entity/component/display/entitymaterial.h index c4bd1e4f..341ff0d0 100644 --- a/src/dusk/entity/component/display/entitymaterial.h +++ b/src/dusk/entity/component/display/entitymaterial.h @@ -60,4 +60,17 @@ void entityMaterialSetShader( const entityid_t entityId, const componentid_t componentId, shader_t *shader +); + +/** + * Sets the unlit color for the given entity and component. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param color The color to set. + */ +void entityMaterialSetColor( + const entityid_t entityId, + const componentid_t componentId, + const color_t color ); \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitymesh.c b/src/dusk/entity/component/display/entitymesh.c index cab59b3c..fa5f2882 100644 --- a/src/dusk/entity/component/display/entitymesh.c +++ b/src/dusk/entity/component/display/entitymesh.c @@ -5,9 +5,13 @@ * https://opensource.org/licenses/MIT */ +#include "entitymesh.h" #include "entity/entitymanager.h" - +#include "assert/assert.h" +#include "util/memory.h" #include "display/mesh/cube.h" +#include "display/mesh/plane.h" +#include "display/mesh/capsule.h" void entityMeshInit( const entityid_t entityId, @@ -17,6 +21,8 @@ void entityMeshInit( entityId, componentId, COMPONENT_TYPE_MESH ); comp->mesh = &CUBE_MESH_SIMPLE; + comp->ownedVertices = NULL; + comp->ownsData = false; } mesh_t * entityMeshGetMesh( @@ -38,4 +44,100 @@ void entityMeshSetMesh( entityId, componentId, COMPONENT_TYPE_MESH ); comp->mesh = mesh; +} + +void entityMeshDispose( + const entityid_t entityId, + const componentid_t componentId +) { + entitymesh_t *comp = componentGetData( + entityId, componentId, COMPONENT_TYPE_MESH + ); + if(!comp->ownsData) return; + (void)meshDispose(&comp->ownedMesh); + memoryFree(comp->ownedVertices); + comp->ownedVertices = NULL; + comp->ownsData = false; + comp->mesh = NULL; +} + +errorret_t entityMeshGeneratePlane( + const entityid_t entityId, + const componentid_t componentId, + const float_t width, + const float_t height +) { + entitymesh_t *comp = componentGetData( + entityId, componentId, COMPONENT_TYPE_MESH + ); + entityMeshDispose(entityId, componentId); + + comp->ownedVertices = memoryAllocate(PLANE_VERTEX_COUNT * sizeof(meshvertex_t)); + assertNotNull(comp->ownedVertices, "Failed to allocate plane vertices"); + + vec3 min = { -width * 0.5f, 0.0f, -height * 0.5f }; + vec3 max = { width * 0.5f, 0.0f, height * 0.5f }; + vec2 uvMin = { 0.0f, 0.0f }; + vec2 uvMax = { 1.0f, 1.0f }; + planeBuffer( + comp->ownedVertices, + PLANE_AXIS_XZ, + min, + max + #if MESH_ENABLE_COLOR + , COLOR_WHITE_4B + #endif + , uvMin, + uvMax + ); + + errorChain(meshInit( + &comp->ownedMesh, + PLANE_PRIMITIVE_TYPE, + PLANE_VERTEX_COUNT, + comp->ownedVertices + )); + + comp->mesh = &comp->ownedMesh; + comp->ownsData = true; + errorOk(); +} + +errorret_t entityMeshGenerateCapsule( + const entityid_t entityId, + const componentid_t componentId, + const float_t radius, + const float_t halfHeight +) { + entitymesh_t *comp = componentGetData( + entityId, componentId, COMPONENT_TYPE_MESH + ); + entityMeshDispose(entityId, componentId); + + comp->ownedVertices = memoryAllocate(CAPSULE_VERTEX_COUNT * sizeof(meshvertex_t)); + assertNotNull(comp->ownedVertices, "Failed to allocate capsule vertices"); + + vec3 center = { 0.0f, 0.0f, 0.0f }; + capsuleBuffer( + comp->ownedVertices, + center, + radius, + halfHeight, + CAPSULE_CAP_RINGS, + CAPSULE_SECTORS + #if MESH_ENABLE_COLOR + , COLOR_WHITE_4B + #endif + ); + + errorChain(meshInit( + &comp->ownedMesh, + CAPSULE_PRIMITIVE_TYPE, + CAPSULE_VERTEX_COUNT, + comp->ownedVertices + )); + + comp->mesh = &comp->ownedMesh; + comp->ownsData = true; + errorOk(); } \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitymesh.h b/src/dusk/entity/component/display/entitymesh.h index 83bc0876..10fb8b5f 100644 --- a/src/dusk/entity/component/display/entitymesh.h +++ b/src/dusk/entity/component/display/entitymesh.h @@ -11,6 +11,9 @@ typedef struct { mesh_t *mesh; + mesh_t ownedMesh; + meshvertex_t *ownedVertices; + bool_t ownsData; } entitymesh_t; /** @@ -47,4 +50,47 @@ void entityMeshSetMesh( const entityid_t entityId, const componentid_t componentId, mesh_t *mesh +); + +/** + * Disposes the entity mesh component, freeing any owned mesh data. + * + * @param entityId The entity ID. + * @param componentId The component ID. + */ +void entityMeshDispose( + const entityid_t entityId, + const componentid_t componentId +); + +/** + * Generates an XZ-aligned plane mesh owned by the component. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param width Width of the plane along the X axis. + * @param height Height of the plane along the Z axis. + * @return Error return value. + */ +errorret_t entityMeshGeneratePlane( + const entityid_t entityId, + const componentid_t componentId, + const float_t width, + const float_t height +); + +/** + * Generates a Y-axis capsule mesh owned by the component. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param radius Radius of the cylinder and hemisphere caps. + * @param halfHeight Half-height of the cylindrical section. + * @return Error return value. + */ +errorret_t entityMeshGenerateCapsule( + const entityid_t entityId, + const componentid_t componentId, + const float_t radius, + const float_t halfHeight ); \ No newline at end of file diff --git a/src/dusk/entity/component/physics/entityphysics.c b/src/dusk/entity/component/physics/entityphysics.c index 631baece..3705f8b7 100644 --- a/src/dusk/entity/component/physics/entityphysics.c +++ b/src/dusk/entity/component/physics/entityphysics.c @@ -100,6 +100,25 @@ bool_t entityPhysicsIsOnGround( return phys->onGround; } +void entityPhysicsSetBodyType( + const entityid_t entityId, + const componentid_t componentId, + const physicsbodytype_t type +) { + entityphysics_t *phys = entityPhysicsGet(entityId, componentId); + assertNotNull(phys, "Failed to get physics component data"); + phys->type = type; +} + +physicsbodytype_t entityPhysicsGetBodyType( + const entityid_t entityId, + const componentid_t componentId +) { + entityphysics_t *phys = entityPhysicsGet(entityId, componentId); + assertNotNull(phys, "Failed to get physics component data"); + return phys->type; +} + void entityPhysicsDispose( const entityid_t entityId, const componentid_t componentId diff --git a/src/dusk/entity/component/physics/entityphysics.h b/src/dusk/entity/component/physics/entityphysics.h index 069f97c3..6cccfca3 100644 --- a/src/dusk/entity/component/physics/entityphysics.h +++ b/src/dusk/entity/component/physics/entityphysics.h @@ -122,6 +122,31 @@ bool_t entityPhysicsIsOnGround( const componentid_t componentId ); +/** + * Sets the body type of the entity's physics body. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param type The body type to set. + */ +void entityPhysicsSetBodyType( + const entityid_t entityId, + const componentid_t componentId, + const physicsbodytype_t type +); + +/** + * Gets the body type of the entity's physics body. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @return The body type of the physics body. + */ +physicsbodytype_t entityPhysicsGetBodyType( + const entityid_t entityId, + const componentid_t componentId +); + /** * Releases the body slot back to PHYSICS_WORLD. Called automatically when * the component is disposed via the component system. diff --git a/src/dusk/entity/component/script/CMakeLists.txt b/src/dusk/entity/component/script/CMakeLists.txt index bd179301..5fa29add 100644 --- a/src/dusk/entity/component/script/CMakeLists.txt +++ b/src/dusk/entity/component/script/CMakeLists.txt @@ -3,7 +3,5 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - entityscript.c -) + + diff --git a/src/dusk/entity/component/script/entityscript.c b/src/dusk/entity/component/script/entityscript.c deleted file mode 100644 index ebbd7323..00000000 --- a/src/dusk/entity/component/script/entityscript.c +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entityscript.h" -#include "entity/entitymanager.h" -#include "assert/assert.h" -#include "util/memory.h" - -void entityScriptInit( - const entityid_t entityId, - const componentid_t compId -) { - entityscript_t *data = componentGetData( - entityId, compId, COMPONENT_TYPE_ENTITYSCRIPT - ); - memoryZero(data, sizeof(entityscript_t)); -} - -void entityScriptUnload( - const entityid_t entityId, - const componentid_t compId -) { - entityscript_t *data = componentGetData( - entityId, compId, COMPONENT_TYPE_ENTITYSCRIPT - ); - - if(!data->active) return; - - lua_getglobal(data->context.luaState, "entityDispose"); - if(lua_isfunction(data->context.luaState, -1)) { - if(lua_pcall(data->context.luaState, 0, 0, 0) != LUA_OK) { - lua_pop(data->context.luaState, 1); - } - } else { - lua_pop(data->context.luaState, 1); - } - - scriptContextDispose(&data->context); - data->active = false; -} - -errorret_t entityScriptSetScript( - const entityid_t entityId, - const componentid_t compId, - const char_t *filename -) { - assertNotNull(filename, "Entity script filename cannot be NULL"); - - entityScriptUnload(entityId, compId); - - entityscript_t *data = componentGetData( - entityId, compId, COMPONENT_TYPE_ENTITYSCRIPT - ); - - errorChain(scriptContextInit(&data->context)); - data->active = true; - - // Expose entity identity to the script as globals - lua_pushnumber(data->context.luaState, (lua_Number)entityId); - lua_setglobal(data->context.luaState, "ENTITY_ID"); - lua_pushnumber(data->context.luaState, (lua_Number)compId); - lua_setglobal(data->context.luaState, "ENTITY_COMPONENT_ID"); - - errorChain(scriptContextExecFile(&data->context, filename)); - - lua_getglobal(data->context.luaState, "entityInit"); - if(lua_isfunction(data->context.luaState, -1)) { - if(lua_pcall(data->context.luaState, 0, 0, 0) != LUA_OK) { - errorThrow( - "Failed to call entityInit: %s", - lua_tostring(data->context.luaState, -1) - ); - } - } else { - lua_pop(data->context.luaState, 1); - } - - errorOk(); -} - -errorret_t entityScriptUpdate( - const entityid_t entityId, - const componentid_t compId -) { - entityscript_t *data = componentGetData( - entityId, compId, COMPONENT_TYPE_ENTITYSCRIPT - ); - - if(!data->active) errorOk(); - - lua_getglobal(data->context.luaState, "entityUpdate"); - if(lua_isfunction(data->context.luaState, -1)) { - if(lua_pcall(data->context.luaState, 0, 0, 0) != LUA_OK) { - errorThrow( - "Failed to call entityUpdate: %s", - lua_tostring(data->context.luaState, -1) - ); - } - } else { - lua_pop(data->context.luaState, 1); - } - - errorOk(); -} - -void entityScriptDispose( - const entityid_t entityId, - const componentid_t compId -) { - entityScriptUnload(entityId, compId); -} diff --git a/src/dusk/entity/component/script/entityscript.h b/src/dusk/entity/component/script/entityscript.h deleted file mode 100644 index c133899c..00000000 --- a/src/dusk/entity/component/script/entityscript.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entity/entitybase.h" -#include "script/scriptcontext.h" - -typedef struct { - scriptcontext_t context; - bool_t active; -} entityscript_t; - -/** - * Initializes the entity script component (empty context, not yet loaded). - * - * @param entityId The entity ID. - * @param compId The component ID. - */ -void entityScriptInit( - const entityid_t entityId, - const componentid_t compId -); - -/** - * Unloads the currently active script: calls entityDispose if defined and - * disposes the context. No-op if no script is loaded. - * - * @param entityId The entity ID. - * @param compId The component ID. - */ -void entityScriptUnload( - const entityid_t entityId, - const componentid_t compId -); - -/** - * Sets the script for this component. If a script is already loaded it is - * unloaded first. Then loads and executes the new file, sets entityId and - * entityScriptId as Lua globals, and calls entityInit if defined. - * - * @param entityId The entity ID. - * @param compId The component ID. - * @param filename The script file to load. - * @return The error return value. - */ -errorret_t entityScriptSetScript( - const entityid_t entityId, - const componentid_t compId, - const char_t *filename -); - -/** - * Calls the entityUpdate Lua function in the entity script context, if defined. - * - * @param entityId The entity ID. - * @param compId The component ID. - * @return The error return value. - */ -errorret_t entityScriptUpdate( - const entityid_t entityId, - const componentid_t compId -); - -/** - * Unloads the script if one is active, then disposes the component. - * - * @param entityId The entity ID. - * @param compId The component ID. - */ -void entityScriptDispose( - const entityid_t entityId, - const componentid_t compId -); diff --git a/src/dusk/entity/componentlist.h b/src/dusk/entity/componentlist.h index 7ce59b84..6fda2fea 100644 --- a/src/dusk/entity/componentlist.h +++ b/src/dusk/entity/componentlist.h @@ -10,11 +10,9 @@ #include "entity/component/display/entitymesh.h" #include "entity/component/display/entitymaterial.h" #include "entity/component/physics/entityphysics.h" -#include "entity/component/script/entityscript.h" X(POSITION, entityposition_t, position, entityPositionInit, NULL) X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL) -X(MESH, entitymesh_t, mesh, entityMeshInit, NULL) +X(MESH, entitymesh_t, mesh, entityMeshInit, entityMeshDispose) X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL) -X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose) -X(ENTITYSCRIPT, entityscript_t, entityscript, entityScriptInit, entityScriptDispose) \ No newline at end of file +X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose) \ No newline at end of file diff --git a/src/dusk/event/event.c b/src/dusk/event/event.c index f0975d5e..0f6bf074 100644 --- a/src/dusk/event/event.c +++ b/src/dusk/event/event.c @@ -8,6 +8,17 @@ #include "event.h" #include "assert/assert.h" #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, @@ -73,7 +84,7 @@ eventsub_t eventSubscribe( const eventcallback_t callback, const void *user ) { - eventSubscribeUser( + return eventSubscribeUser( event, EVENT_TYPE_C, (eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } } @@ -147,30 +158,29 @@ void eventUnsubscribe(event_t *event, const eventsub_t id) { if(event->listenerCount == 0) return; - // Find listener uint16_t index = 0; do { - if(event->listenerArray[index].id == id) { - // Found it, remove by swapping with last and reducing count - event->listenerArray[index] = event->listenerArray[--event->listenerCount]; - return; + if(event->listenerArray[index].id != id) { + index++; + continue; } - index++; + + // 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 + ); + } + } + + // Swap with last and shrink + event->listenerArray[index] = event->listenerArray[--event->listenerCount]; + return; } while(index < event->listenerCount); - - // Did we find it? - if(index == event->listenerCount) return; - - // Shift remaining listeners down (if any) - if(index < event->listenerCount - 1) { - memoryMove( - &event->listenerArray[index], - &event->listenerArray[index + 1], - sizeof(eventlistener_t) * (event->listenerCount - index - 1) - ); - } - - event->listenerCount--; } void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) { @@ -219,29 +229,28 @@ void eventInvoke( if(listener->type == EVENT_TYPE_C) { listener->user.c.callback(&data, listener->user.c); } else if(listener->type == EVENT_TYPE_SCRIPT) { - // Call Lua function lua_State *L = listener->user.script.context->luaState; assertNotNull(L, "Lua state in event listener cannot be NULL"); - // Push function + lua_pushcfunction(L, eventLuaTraceback); + int handlerIdx = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef); - if(eventParams != NULL && metatableName != NULL) { - lua_getmetatable(L, -1); - luaL_getmetatable(L, metatableName); - assertTrue( - lua_rawequal(L, -1, -2), - "Event parameter metatable does not match expected type" - ); + int numArgs = 0; + if(eventParams != NULL) { + lua_pushlightuserdata(L, (void *)eventParams); + numArgs = 1; } - // Call function with 1 arg, 0 return values - if(lua_pcall(L, 1, 0, 0) != LUA_OK) { + printf("Invoking Lua event listener with %d argument(s)\n", numArgs); + + 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); - // Log error but continue - printf("Error invoking Lua event listener: %s\n", strErr); } + + lua_pop(L, 1); } else { assertUnreachable("Unknown event listener type"); } diff --git a/src/dusk/input/input.c b/src/dusk/input/input.c index beb83e8b..e7fdb267 100644 --- a/src/dusk/input/input.c +++ b/src/dusk/input/input.c @@ -95,27 +95,27 @@ void inputUpdate(void) { if(TIME.dynamicUpdate) return; #endif - if(INPUT.eventPressed.listenerCount > 0) { - action = &INPUT.actions[0]; - do { - if(inputPressed(action->action)) { - inputevent_t inputEvent = { .action = action->action }; - eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt"); - } - action++; - } while(action < &INPUT.actions[INPUT_ACTION_COUNT]); - } + // if(INPUT.eventPressed.listenerCount > 0) { + // action = &INPUT.actions[0]; + // do { + // if(inputPressed(action->action)) { + // inputevent_t inputEvent = { .action = action->action }; + // eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt"); + // } + // action++; + // } while(action < &INPUT.actions[INPUT_ACTION_COUNT]); + // } - if(INPUT.eventReleased.listenerCount > 0) { - action = &INPUT.actions[0]; - do { - if(inputReleased(action->action)) { - inputevent_t inputEvent = { .action = action->action }; - eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt"); - } - action++; - } while(action < &INPUT.actions[INPUT_ACTION_COUNT]); - } + // if(INPUT.eventReleased.listenerCount > 0) { + // action = &INPUT.actions[0]; + // do { + // if(inputReleased(action->action)) { + // inputevent_t inputEvent = { .action = action->action }; + // eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt"); + // } + // action++; + // } while(action < &INPUT.actions[INPUT_ACTION_COUNT]); + // } } float_t inputGetCurrentValue(const inputaction_t action) { diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index fb77aba0..d147522e 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -1,5 +1,5 @@ // Copyright (c) 2026 Dominic Masters -// +// // This software is released under the MIT License. // https://opensource.org/licenses/MIT @@ -17,11 +17,61 @@ #include "display/screen/screen.h" #include "console/console.h" #include "util/string.h" +#include "script/scriptmanager.h" scene_t SCENE; +// Releases the current scene class ref and rebuilds Scene.set global. +static void sceneReset(void) { + lua_State *L = SCRIPT_MANAGER.mainContext.luaState; + if(SCENE.scriptRef != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, SCENE.scriptRef); + SCENE.scriptRef = LUA_NOREF; + } + lua_newtable(L); + lua_pushcfunction(L, sceneSetLua); + lua_setfield(L, -2, "set"); + lua_setglobal(L, "Scene"); +} + +// Calls sceneClass:method() if it exists. Returns an error if the call fails. +static errorret_t sceneCall(const char_t *method) { + lua_State *L = SCRIPT_MANAGER.mainContext.luaState; + + if(SCENE.scriptRef == LUA_NOREF || SCENE.scriptRef == LUA_REFNIL) { + errorOk(); + } + + 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); + } + + lua_getfield(L, -1, method); + + if(!lua_isfunction(L, -1)) { + lua_pop(L, 2); + errorOk(); + } + + lua_pushvalue(L, -2); // self + + if(lua_pcall(L, 1, 0, 0) != LUA_OK) { + const char_t *err = lua_tostring(L, -1); + lua_pop(L, 2); // error + scene table + errorThrow("Scene:%s failed: %s", method, err); + } + + lua_pop(L, 1); // scene table + errorOk(); +} + errorret_t sceneInit(void) { memoryZero(&SCENE, sizeof(scene_t)); + SCENE.scriptRef = LUA_NOREF; + sceneReset(); errorOk(); } @@ -32,27 +82,14 @@ errorret_t sceneUpdate(void) { } #endif - // Is the scene changing? if(stringCompare(SCENE.sceneNext, SCENE.sceneCurrent) != 0) { errorChain(sceneSetImmediate(SCENE.sceneNext)); } - // Call scene update function if script loaded - if(!SCENE.scriptActive) { - errorOk(); + if(SCENE.scriptActive) { + errorChain(sceneCall("update")); } - assertNotNull(SCENE.script.luaState, "Script context null?"); - lua_getglobal(SCENE.script.luaState, "sceneUpdate"); - if(lua_isfunction(SCENE.script.luaState, -1)) { - if(lua_pcall(SCENE.script.luaState, 0, 0, 0) != LUA_OK) { - errorThrow( - "Failed to call sceneUpdate function in script: %s\n", - lua_tostring(SCENE.script.luaState, -1) - ); - } - } - errorOk(); } @@ -163,80 +200,57 @@ errorret_t sceneRender(void) { errorOk(); } -errorret_t sceneSetImmediate(const char_t *script) { - // Update the next scene name (shouldn't really happen but its for safety). - if(script != SCENE.sceneNext) { +errorret_t sceneSetImmediate(const char_t *scene) { + if(scene != SCENE.sceneNext) { stringCopy( SCENE.sceneNext, - script == NULL ? "" : script, + scene == NULL ? "" : scene, ASSET_FILE_PATH_MAX ); } - // If there's currently a scene active, dispose of it first. if(SCENE.scriptActive) { - assertNotNull(SCENE.script.luaState, "Script context null?"); - - // Call sceneDispose function - lua_getglobal(SCENE.script.luaState, "sceneDispose"); - if(lua_isfunction(SCENE.script.luaState, -1)) { - if(lua_pcall(SCENE.script.luaState, 0, 0, 0) != LUA_OK) { - errorThrow( - "Failed to call sceneDispose function in script: %s\n", - lua_tostring(SCENE.script.luaState, -1) - ); - } - } - - // Scenes should ideally clean themselves up, this is just a warning because - // you can technically move entities between scenes, but it's messy. - for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) { - if(ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) { - consolePrint( - "Entity %d is active after scene dispose\n", i - ); - } - } - - // Unload script context. - scriptContextDispose(&SCENE.script); + errorChain(sceneCall("dispose")); SCENE.scriptActive = false; } - // At this point the existing scene is unloaded, update the current scene. + // 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. + sceneReset(); + stringCopy( SCENE.sceneCurrent, - script == NULL ? "" : script, + scene == NULL ? "" : scene, ASSET_FILE_PATH_MAX ); - // If we have a new scene, execute it. - if(script != NULL) { - errorChain(scriptContextInit(&SCENE.script)); - SCENE.scriptActive = true; - errorChain(scriptContextExecFile(&SCENE.script, script)); + if(scene != NULL) { + lua_State *L = SCRIPT_MANAGER.mainContext.luaState; - // Call the init function. - lua_getglobal(SCENE.script.luaState, "sceneInit"); - if(lua_isfunction(SCENE.script.luaState, -1)) { - if(lua_pcall(SCENE.script.luaState, 0, 0, 0) != LUA_OK) { - errorThrow( - "Failed to call sceneInit function in script: %s\n", - lua_tostring(SCENE.script.luaState, -1) - ); - } + 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); } + lua_settop(L, stackBase + 1); + + SCENE.scriptRef = luaL_ref(L, LUA_REGISTRYINDEX); + + errorChain(sceneCall("init")); + SCENE.scriptActive = true; } errorOk(); } -void sceneSet(const char_t *script) { - // Tell the scene that next update it needs to change scenes. This will always - // occur on the NEXT frame. +void sceneSet(const char_t *scene) { stringCopy( SCENE.sceneNext, - script == NULL ? "" : script, + scene == NULL ? "" : scene, ASSET_FILE_PATH_MAX ); } @@ -244,4 +258,16 @@ void sceneSet(const char_t *script) { errorret_t sceneDispose(void) { errorChain(sceneSetImmediate(NULL)); errorOk(); -} \ No newline at end of file +} + +int sceneSetLua(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; + } + + sceneSet(lua_tostring(L, 1)); + return 0; +} diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h index f52ba82a..9b79e5d6 100644 --- a/src/dusk/scene/scene.h +++ b/src/dusk/scene/scene.h @@ -1,6 +1,6 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ @@ -8,54 +8,23 @@ #pragma once #include "script/scriptcontext.h" #include "asset/assetfile.h" +#include "event/event.h" + +#define SCENE_EVENT_UPDATE_MAX 16 typedef struct { - scriptcontext_t script; bool_t scriptActive; + int scriptRef; char_t sceneCurrent[ASSET_FILE_PATH_MAX]; char_t sceneNext[ASSET_FILE_PATH_MAX]; } scene_t; extern scene_t SCENE; -/** - * Initialize the scene subsystem. - * - * @return The error return value. - */ errorret_t sceneInit(void); - -/** - * Update the current scene. - * - * @return The error return value. - */ errorret_t sceneUpdate(void); - -/** - * Render the current scene. - * - * @return The error return value. - */ errorret_t sceneRender(void); - -/** - * Internal only method, immediately sets the scene. This will basically crash - * Lua if called from its tree. - */ -errorret_t sceneSetImmediate(const char_t *script); - -/** - * Set the current scene by script name. - * - * @param script The script name of the scene to set. - * @return The error return value. - */ -void sceneSet(const char_t *script); - -/** - * Dispose of the scene subsystem. - * - * @return The error return value. - */ -errorret_t sceneDispose(void); \ No newline at end of file +errorret_t sceneSetImmediate(const char_t *scene); +void sceneSet(const char_t *scene); +errorret_t sceneDispose(void); +int sceneSetLua(lua_State *L); diff --git a/src/dusk/script/CMakeLists.txt b/src/dusk/script/CMakeLists.txt index e086520c..91f823f5 100644 --- a/src/dusk/script/CMakeLists.txt +++ b/src/dusk/script/CMakeLists.txt @@ -8,8 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC scriptmanager.c scriptcontext.c - scriptmodule.c ) -# Subdirectories -add_subdirectory(module) \ No newline at end of file +# Subdirectories \ No newline at end of file diff --git a/src/dusk/script/module/CMakeLists.txt b/src/dusk/script/module/CMakeLists.txt deleted file mode 100644 index 4780d660..00000000 --- a/src/dusk/script/module/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Subdirectories -add_subdirectory(display) -add_subdirectory(entity) -add_subdirectory(event) -add_subdirectory(input) -add_subdirectory(locale) -add_subdirectory(system) -add_subdirectory(scene) -add_subdirectory(time) -add_subdirectory(ui) \ No newline at end of file diff --git a/src/dusk/script/module/display/CMakeLists.txt b/src/dusk/script/module/display/CMakeLists.txt deleted file mode 100644 index 8283b3e5..00000000 --- a/src/dusk/script/module/display/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleglm.c - modulespritebatch.c - moduleglm.c - modulecolor.c - moduletext.c - modulescreen.c - moduletileset.c - moduletexture.c - moduleshader.c -) \ No newline at end of file diff --git a/src/dusk/script/module/display/modulecolor.c b/src/dusk/script/module/display/modulecolor.c deleted file mode 100644 index 8fe4f0f5..00000000 --- a/src/dusk/script/module/display/modulecolor.c +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecolor.h" -#include "display/color.h" -#include "assert/assert.h" -#include "util/string.h" -#include "time/time.h" - -void moduleColor(scriptcontext_t *context) { - assertNotNull(context, "Context cannot be NULL."); - - if(luaL_newmetatable(context->luaState, "color_mt")) { - // Metatable is new, set __index and __newindex - lua_pushstring(context->luaState, "__index"); - lua_pushcfunction(context->luaState, moduleColorIndex); - lua_settable(context->luaState, -3); - - lua_pushstring(context->luaState, "__newindex"); - lua_pushcfunction(context->luaState, moduleColorNewIndex); - lua_settable(context->luaState, -3); - - lua_pushstring(context->luaState, "__tostring"); - lua_pushcfunction(context->luaState, moduleColorToString); - lua_settable(context->luaState, -3); - } - lua_pop(context->luaState, 1); - - lua_register(context->luaState, "color", moduleColorFuncColor); - lua_register(context->luaState, "colorRainbow", moduleColorRainbow); - - scriptContextExec(context, COLOR_SCRIPT); -} - -int moduleColorFuncColor(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL."); - - scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L); - assertNotNull(context, "Script context cannot be NULL."); - - // Needs 4 channel uint8_t - if( - !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || - !lua_isnumber(L, 3) || !lua_isnumber(L, 4) - ) { - return luaL_error(L, "color(r, g, b, a) requires four number arguments."); - } - - colorchannel8_t r = (colorchannel8_t)lua_tonumber(L, 1); - colorchannel8_t g = (colorchannel8_t)lua_tonumber(L, 2); - colorchannel8_t b = (colorchannel8_t)lua_tonumber(L, 3); - colorchannel8_t a = (colorchannel8_t)lua_tonumber(L, 4); - - // Create color_t as lua memory, and push metatable - color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t)); - luaL_getmetatable(L, "color_mt"); - lua_setmetatable(L, -2); - - // Initial values. - color->r = r; - color->g = g; - color->b = b; - color->a = a; - - return 1; -} - -int moduleColorIndex(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL."); - - const char_t *key = lua_tostring(L, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - const color_t *color = (const color_t*)luaL_checkudata(L, 1, "color_mt"); - assertNotNull(color, "Color struct cannot be NULL."); - - if(stringCompare(key, "r") == 0) { - lua_pushnumber(L, color->r); - return 1; - } - if(stringCompare(key, "g") == 0) { - lua_pushnumber(L, color->g); - return 1; - } - if(stringCompare(key, "b") == 0) { - lua_pushnumber(L, color->b); - return 1; - } - if(stringCompare(key, "a") == 0) { - lua_pushnumber(L, color->a); - return 1; - } - - lua_pushnil(L); - return 1; -} - -int moduleColorNewIndex(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL."); - - const char_t *key = lua_tostring(L, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt"); - assertNotNull(color, "Color struct cannot be NULL."); - - if(stringCompare(key, "r") == 0) { - if(!lua_isnumber(L, 3)) { - return luaL_error(L, "color channel r must be a number."); - } - color->r = (colorchannel8_t)lua_tonumber(L, 3); - return 0; - } else if(stringCompare(key, "g") == 0) { - if(!lua_isnumber(L, 3)) { - return luaL_error(L, "color channel g must be a number."); - } - color->g = (colorchannel8_t)lua_tonumber(L, 3); - return 0; - } else if(stringCompare(key, "b") == 0) { - if(!lua_isnumber(L, 3)) { - return luaL_error(L, "color channel b must be a number."); - } - color->b = (colorchannel8_t)lua_tonumber(L, 3); - return 0; - } else if(stringCompare(key, "a") == 0) { - if(!lua_isnumber(L, 3)) { - return luaL_error(L, "color channel a must be a number."); - } - color->a = (colorchannel8_t)lua_tonumber(L, 3); - return 0; - } - - return 0; -} - -int moduleColorToString(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL."); - - const color_t *color = (const color_t*)luaL_checkudata(L, 1, "color_mt"); - assertNotNull(color, "Color struct cannot be NULL."); - - lua_pushfstring( - L, "color(r=%d, g=%d, b=%d, a=%d)", - color->r, color->g, color->b, color->a - ); - - return 1; -} - -int moduleColorRainbow(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL."); - - // Allow time offset - float_t t = TIME.time; - if(lua_gettop(L) >= 1) { - if(!lua_isnumber(L, 1)) { - return luaL_error(L, "Rainbow time offset must be a number."); - } - t += (float_t)lua_tonumber(L, 1); - } - - // Allow speed multiplier - if(lua_gettop(L) >= 2) { - if(!lua_isnumber(L, 2)) { - return luaL_error(L, "Rainbow speed multiplier must be a number."); - } - t *= (float_t)lua_tonumber(L, 2); - } - - // Generate rainbow based on time. - color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t)); - color->r = (colorchannel8_t)((sinf(t) + 1.0f) * 0.5f * 255.0f); - color->g = (colorchannel8_t)((sinf(t + 2.0f) + 1.0f) * 0.5f * 255.0f); - color->b = (colorchannel8_t)((sinf(t + 4.0f) + 1.0f) * 0.5f * 255.0f); - color->a = 255; - - // Set metatable - luaL_getmetatable(L, "color_mt"); - lua_setmetatable(L, -2); - - return 1; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/modulecolor.h b/src/dusk/script/module/display/modulecolor.h index 950be54b..de7838d3 100644 --- a/src/dusk/script/module/display/modulecolor.h +++ b/src/dusk/script/module/display/modulecolor.h @@ -1,55 +1,121 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "display/color.h" +#include "time/time.h" -/** - * Registers the color module with the given script context. - * - * @param context The script context to register the module with. - */ -void moduleColor(scriptcontext_t *context); +static 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); -/** - * Lua function to create a color. - * - * @param L The Lua state. - * @return Number of return values. - */ -int moduleColorFuncColor(lua_State *L); + 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; } -/** - * Index function for the color structure. - * @param L The Lua state. - * @return Number of return values. - */ -int moduleColorIndex(lua_State *L); + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + return 1; +} -/** - * New index function for the color structure. - * - * @param L The Lua state. - * @return Number of return values. - */ -int moduleColorNewIndex(lua_State *L); +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); -/** - * Color to string method for script - * - * @param L The Lua state. - * @return Number of return values. - */ -int moduleColorToString(lua_State *L); + 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; + } -/** - * Lua function to create a rainbow color based on time. - * - * @param L The Lua state. - * @return Number of return values. - */ -int moduleColorRainbow(lua_State *L); \ No newline at end of file + 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 + ); + return 1; +} + +static int moduleColorFuncColor(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."); + } + + 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; +} + +static int moduleColorRainbow(lua_State *L) { + 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(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); + } + + 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; +} + +static void moduleColor(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + + 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", moduleColorFuncColor); + lua_register(L, "colorRainbow", moduleColorRainbow); + + luaL_dostring(L, COLOR_SCRIPT); +} diff --git a/src/dusk/script/module/display/moduleglm.c b/src/dusk/script/module/display/moduleglm.c deleted file mode 100644 index 0a5af923..00000000 --- a/src/dusk/script/module/display/moduleglm.c +++ /dev/null @@ -1,283 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleglm.h" -#include "assert/assert.h" -#include "util/string.h" -#include "util/memory.h" - -void moduleGLM(scriptcontext_t *context) { - assertNotNull(context, "Context cannot be NULL."); - - // Create metatable for vec3 structure. - if(luaL_newmetatable(context->luaState, "vec3_mt")) { - // Metatable methods - lua_pushcfunction(context->luaState, moduleVec3Index); - lua_setfield(context->luaState, -2, "__index"); - lua_pushcfunction(context->luaState, moduleVec3NewIndex); - lua_setfield(context->luaState, -2, "__newindex"); - lua_pushcfunction(context->luaState, moduleVec3ToString); - lua_setfield(context->luaState, -2, "__tostring"); - } - lua_pop(context->luaState, 1); - - if(luaL_newmetatable(context->luaState, "vec4_mt")) { - // Metatable methods - lua_pushcfunction(context->luaState, moduleVec4Index); - lua_setfield(context->luaState, -2, "__index"); - lua_pushcfunction(context->luaState, moduleVec4NewIndex); - lua_setfield(context->luaState, -2, "__newindex"); - lua_pushcfunction(context->luaState, moduleVec4ToString); - lua_setfield(context->luaState, -2, "__tostring"); - } - lua_pop(context->luaState, 1); - - lua_register(context->luaState, "vec3", moduleVec3Create); -} - - -int moduleVec3Create(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3)); - memoryZero(v, sizeof(vec3)); - - // May be expecting between 1 and 3 values. - int top = lua_gettop(l); - if(top >= 1) { - if(!lua_isnumber(l, 1)) { - luaL_error(l, "Vec3 x component must be a number."); - } - (*v)[0] = (float_t)lua_tonumber(l, 1); - } - if(top >= 2) { - if(!lua_isnumber(l, 2)) { - luaL_error(l, "Vec3 y component must be a number."); - } - (*v)[1] = (float_t)lua_tonumber(l, 2); - } - if(top >= 3) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec3 z component must be a number."); - } - (*v)[2] = (float_t)lua_tonumber(l, 3); - } - - // Set metatable - luaL_getmetatable(l, "vec3_mt"); - lua_setmetatable(l, -2); - - return 1; -} - -int moduleVec3Index(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = lua_tostring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); - assertNotNull(vec, "Vec3 pointer cannot be NULL."); - - if(stringCompare(key, "x") == 0) { - lua_pushnumber(l, (*vec)[0]); - return 1; - } else if(stringCompare(key, "y") == 0) { - lua_pushnumber(l, (*vec)[1]); - return 1; - } else if(stringCompare(key, "z") == 0) { - lua_pushnumber(l, (*vec)[2]); - return 1; - } -} - -int moduleVec3NewIndex(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = luaL_checkstring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); - assertNotNull(vec, "Vec3 pointer cannot be NULL."); - - if(stringCompare(key, "x") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec3 x component must be a number."); - } - (*vec)[0] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "y") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec3 y component must be a number."); - } - (*vec)[1] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "z") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec3 z component must be a number."); - } - (*vec)[2] = (float_t)lua_tonumber(l, 3); - return 0; - } - - luaL_error(l, "Invalid key for vec3: %s", key); - return 0; -} - -int moduleVec3ToString(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); - assertNotNull(vec, "Vec3 pointer cannot be NULL."); - - char buf[128]; - snprintf( - buf, sizeof(buf), - "vec3(%.3f, %.3f, %.3f)", - (*vec)[0], (*vec)[1], (*vec)[2] - ); - lua_pushstring(l, buf); - - return 1; -} - - - -int moduleVec4Create(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - vec4 *v = (vec4 *)lua_newuserdata(l, sizeof(vec4)); - memoryZero(v, sizeof(vec4)); - - // May be expecting between 1 and 4 values. - int top = lua_gettop(l); - if(top >= 1) { - if(!lua_isnumber(l, 1)) { - luaL_error(l, "Vec4 x component must be a number."); - } - (*v)[0] = (float_t)lua_tonumber(l, 1); - } - if(top >= 2) { - if(!lua_isnumber(l, 2)) { - luaL_error(l, "Vec4 y component must be a number."); - } - (*v)[1] = (float_t)lua_tonumber(l, 2); - } - if(top >= 3) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 z component must be a number."); - } - (*v)[2] = (float_t)lua_tonumber(l, 3); - } - if(top >= 4) { - if(!lua_isnumber(l, 4)) { - luaL_error(l, "Vec4 w component must be a number."); - } - (*v)[3] = (float_t)lua_tonumber(l, 4); - } - - // Set metatable - luaL_getmetatable(l, "vec4_mt"); - lua_setmetatable(l, -2); - - return 1; -} - -int moduleVec4Index(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = lua_tostring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt"); - assertNotNull(vec, "Vec4 pointer cannot be NULL."); - - if(stringCompare(key, "x") == 0) { - lua_pushnumber(l, (*vec)[0]); - return 1; - } else if(stringCompare(key, "y") == 0) { - lua_pushnumber(l, (*vec)[1]); - return 1; - } else if(stringCompare(key, "z") == 0) { - lua_pushnumber(l, (*vec)[2]); - return 1; - } else if(stringCompare(key, "w") == 0) { - lua_pushnumber(l, (*vec)[3]); - return 1; - } else if(stringCompare(key, "u0") == 0) { - lua_pushnumber(l, (*vec)[0]); - return 1; - } else if(stringCompare(key, "v0") == 0) { - lua_pushnumber(l, (*vec)[1]); - return 1; - } else if(stringCompare(key, "u1") == 0) { - lua_pushnumber(l, (*vec)[2]); - return 1; - } else if(stringCompare(key, "v1") == 0) { - lua_pushnumber(l, (*vec)[3]); - return 1; - } - - lua_pushnil(l); - return 1; -} - -int moduleVec4NewIndex(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = luaL_checkstring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt"); - assertNotNull(vec, "Vec4 pointer cannot be NULL."); - - if(stringCompare(key, "x") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 x component must be a number."); - } - (*vec)[0] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "y") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 y component must be a number."); - } - (*vec)[1] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "z") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 z component must be a number."); - } - (*vec)[2] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "w") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 w component must be a number."); - } - (*vec)[3] = (float_t)lua_tonumber(l, 3); - return 0; - } - - luaL_error(l, "Invalid key for vec4: %s", key); - return 0; -} - -int moduleVec4ToString(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt"); - assertNotNull(vec, "Vec4 pointer cannot be NULL."); - - char buf[128]; - snprintf( - buf, sizeof(buf), - "vec4(%.3f, %.3f, %.3f, %.3f)", - (*vec)[0], (*vec)[1], (*vec)[2], (*vec)[3] - ); - lua_pushstring(l, buf); - - return 1; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/moduleglm.h b/src/dusk/script/module/display/moduleglm.h deleted file mode 100644 index 12eb85ed..00000000 --- a/src/dusk/script/module/display/moduleglm.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -/** - * Registers the GLM module with the given script context. - * - * @param context The script context to register the module with. - */ -void moduleGLM(scriptcontext_t *context); - -/** - * Creates a new vec3 structure in Lua. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec3Create(lua_State *l); - -/** - * Lua __index metamethod for vec3 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec3Index(lua_State *l); - -/** - * Lua __newindex metamethod for vec3 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec3NewIndex(lua_State *l); - -/** - * Lua __tostring metamethod for vec3 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec3ToString(lua_State *l); - -/** - * Creates a new vec4 structure in Lua. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec4Create(lua_State *l); - -/** - * Lua __index metamethod for vec4 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec4Index(lua_State *l); - -/** - * Lua __newindex metamethod for vec4 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec4NewIndex(lua_State *l); - -/** - * Lua __tostring metamethod for vec4 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec4ToString(lua_State *l); \ No newline at end of file diff --git a/src/dusk/script/module/display/modulescreen.c b/src/dusk/script/module/display/modulescreen.c deleted file mode 100644 index 1ea23c4e..00000000 --- a/src/dusk/script/module/display/modulescreen.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulescreen.h" -#include "assert/assert.h" -#include "display/screen/screen.h" - -void moduleScreen(scriptcontext_t *context) { - assertNotNull(context, "Context cannot be NULL."); - - lua_register(context->luaState, "screenGetWidth", moduleScreenGetWidth); - lua_register(context->luaState, "screenGetHeight", moduleScreenGetHeight); - lua_register(context->luaState, "screenSetBackground", moduleScreenSetBackground); -} - -int moduleScreenGetWidth(lua_State *L) { - assertNotNull(L, "Lua state is null"); - lua_pushnumber(L, SCREEN.width); - return 1; -} - -int moduleScreenGetHeight(lua_State *L) { - assertNotNull(L, "Lua state is null"); - lua_pushnumber(L, SCREEN.height); - return 1; -} - -int moduleScreenSetBackground(lua_State *L) { - assertNotNull(L, "Lua state is null"); - - if(!lua_isuserdata(L, 1)) { - luaL_error(L, "Screen background color must be a color struct"); - return 0; - } - - color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt"); - SCREEN.background = *color; - return 0; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/modulescreen.h b/src/dusk/script/module/display/modulescreen.h index baa7e662..021f0f5f 100644 --- a/src/dusk/script/module/display/modulescreen.h +++ b/src/dusk/script/module/display/modulescreen.h @@ -1,40 +1,42 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "display/screen/screen.h" -/** - * Registers the Screen module with the given script context. - * - * @param context The script context to register the module with. - */ -void moduleScreen(scriptcontext_t *context); +static int moduleScreenGetWidth(lua_State *L) { + assertNotNull(L, "Lua state is null"); + lua_pushnumber(L, SCREEN.width); + return 1; +} -/** - * Gets the current screen width. - * - * @param L The Lua state. - * @return Count of return values. - */ -int moduleScreenGetWidth(lua_State *L); +static int moduleScreenGetHeight(lua_State *L) { + assertNotNull(L, "Lua state is null"); + lua_pushnumber(L, SCREEN.height); + return 1; +} -/** - * Gets the current screen height. - * - * @param L The Lua state. - * @return Count of return values. - */ -int moduleScreenGetHeight(lua_State *L); +static int moduleScreenSetBackground(lua_State *L) { + assertNotNull(L, "Lua state is null"); -/** - * Sets the screen background color. - * - * @param L The Lua state. - * @return Count of return values. - */ -int moduleScreenSetBackground(lua_State *L); \ No newline at end of file + if(!lua_isuserdata(L, 1)) { + luaL_error(L, "Screen background color must be a color struct"); + return 0; + } + + color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt"); + SCREEN.background = *color; + return 0; +} + +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); +} diff --git a/src/dusk/script/module/display/moduleshader.c b/src/dusk/script/module/display/moduleshader.c deleted file mode 100644 index e114d229..00000000 --- a/src/dusk/script/module/display/moduleshader.c +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleshader.h" -#include "assert/assert.h" -#include "display/shader/shader.h" -#include "display/shader/shaderunlit.h" - -void moduleShader(scriptcontext_t *context) { - assertNotNull(context, "Context cannot be NULL."); - - // Shader unlit defs - lua_pushlightuserdata(context->luaState, &SHADER_UNLIT); - lua_setglobal(context->luaState, "SHADER_UNLIT"); - - lua_pushstring(context->luaState, SHADER_UNLIT_PROJECTION); - lua_setglobal(context->luaState, "SHADER_UNLIT_PROJECTION"); - lua_pushstring(context->luaState, SHADER_UNLIT_VIEW); - lua_setglobal(context->luaState, "SHADER_UNLIT_VIEW"); - lua_pushstring(context->luaState, SHADER_UNLIT_MODEL); - lua_setglobal(context->luaState, "SHADER_UNLIT_MODEL"); - lua_pushstring(context->luaState, SHADER_UNLIT_TEXTURE); - lua_setglobal(context->luaState, "SHADER_UNLIT_TEXTURE"); - - // Shader methods - lua_register(context->luaState, "shaderBind", moduleShaderBind); - lua_register(context->luaState, "shaderSetMatrix", moduleShaderSetMatrix); - lua_register(context->luaState, "shaderSetTexture", moduleShaderSetTexture); -} - -int moduleShaderBind(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - // Should be passed a shader userdata pointer only. - shader_t *shader = (shader_t *)lua_touserdata(l, 1); - assertNotNull(shader, "Shader pointer cannot be NULL."); - - errorret_t ret = shaderBind(shader); - if(ret.code != ERROR_OK) { - luaL_error(l, "Failed to bind shader: %s", ret.state->message); - errorCatch(errorPrint(ret)); - return 0; - } - - return 0; -} - -int moduleShaderSetMatrix(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - // Expect shader, string and matrix. - if(!lua_isuserdata(l, 1)) { - luaL_error(l, "First argument must be a shader_mt userdata."); - return 0; - } - - if(!lua_isstring(l, 2)) { - luaL_error(l, "Second argument must be a string."); - return 0; - } - - if(!lua_isuserdata(l, 3)) { - luaL_error(l, "Third argument must be a mat4_mt userdata."); - return 0; - } - - shader_t *shader = (shader_t *)lua_touserdata(l, 1); - assertNotNull(shader, "Shader pointer cannot be NULL."); - - const char_t *uniformName = luaL_checkstring(l, 2); - assertStrLenMin(uniformName, 1, "Uniform name cannot be empty."); - - mat4 *mat = (mat4 *)lua_touserdata(l, 3); - assertNotNull(mat, "Matrix pointer cannot be NULL."); - - errorret_t ret = shaderSetMatrix(shader, uniformName, *mat); - if(ret.code != ERROR_OK) { - luaL_error(l, "Failed to set shader matrix: %s", ret.state->message); - errorCatch(errorPrint(ret)); - return 0; - } - - return 0; -} - -int moduleShaderSetTexture(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - shader_t *shader = (shader_t *)lua_touserdata(l, 1); - assertNotNull(shader, "Shader pointer cannot be NULL."); - - const char_t *uniformName = luaL_checkstring(l, 2); - assertStrLenMin(uniformName, 1, "Uniform name cannot be empty."); - - texture_t *texture; - // Texture can be Nil or a pointer, if not nil it must be a texture pointer. - if(lua_isnil(l, 3)) { - texture = NULL; - } else if(lua_isuserdata(l, 3)) { - texture = (texture_t *)lua_touserdata(l, 3); - assertNotNull(texture, "Texture pointer cannot be NULL."); - } else { - luaL_error(l, "Third argument must be a texture_mt userdata or nil."); - return 0; - } - - errorret_t ret = shaderSetTexture(shader, uniformName, texture); - if(ret.code != ERROR_OK) { - luaL_error(l, "Failed to set shader texture: %s", ret.state->message); - errorCatch(errorPrint(ret)); - return 0; - } - - return 0; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/moduleshader.h b/src/dusk/script/module/display/moduleshader.h index d50e33c0..7a19a8ba 100644 --- a/src/dusk/script/module/display/moduleshader.h +++ b/src/dusk/script/module/display/moduleshader.h @@ -1,42 +1,90 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "display/shader/shader.h" +#include "display/shader/shaderunlit.h" +#include "script/module/math/modulemat4.h" -/** - * Register shader functions to the given script context. - * - * @param context The script context to register shader functions to. - */ -void moduleShader(scriptcontext_t *context); +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."); -/** - * Script binding for binding a shader. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleShaderBind(lua_State *l); + errorret_t ret = shaderBind(shader); + if(ret.code != ERROR_OK) { + luaL_error(l, "Failed to bind shader: %s", ret.state->message); + errorCatch(errorPrint(ret)); + return 0; + } + return 0; +} -/** - * Script binding for setting a matrix uniform in a shader. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleShaderSetMatrix(lua_State *l); +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."); -/** - * Script binding for setting a texture uniform in a shader. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleShaderSetTexture(lua_State *l); + errorret_t ret = shaderSetMatrix(shader, uniformName, *mat); + if(ret.code != ERROR_OK) { + luaL_error(l, "Failed to set shader matrix: %s", ret.state->message); + errorCatch(errorPrint(ret)); + return 0; + } + return 0; +} -errorret_t doThing(); \ No newline at end of file +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."); + + 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; + } + + errorret_t ret = shaderSetTexture(shader, uniformName, texture); + if(ret.code != ERROR_OK) { + luaL_error(l, "Failed to set shader texture: %s", ret.state->message); + errorCatch(errorPrint(ret)); + return 0; + } + return 0; +} + +static void moduleShader(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + + lua_pushlightuserdata(L, &SHADER_UNLIT); + lua_setglobal(L, "SHADER_UNLIT"); + + 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"); + + lua_register(L, "shaderBind", moduleShaderBind); + lua_register(L, "shaderSetMatrix", moduleShaderSetMatrix); + lua_register(L, "shaderSetTexture", moduleShaderSetTexture); +} diff --git a/src/dusk/script/module/display/modulespritebatch.c b/src/dusk/script/module/display/modulespritebatch.c deleted file mode 100644 index c042c1fd..00000000 --- a/src/dusk/script/module/display/modulespritebatch.c +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulespritebatch.h" -#include "display/spritebatch/spritebatch.h" -#include "assert/assert.h" - -void moduleSpriteBatch(scriptcontext_t *context) { - lua_register(context->luaState, "spriteBatchFlush", moduleSpriteBatchFlush); - lua_register(context->luaState, "spriteBatchClear", moduleSpriteBatchClear); - lua_register(context->luaState, "spriteBatchPush", moduleSpriteBatchPush); -} - -int moduleSpriteBatchFlush(lua_State *L) { - assertNotNull(L, "Lua state is null"); - - spriteBatchFlush(); - return 0; -} - -int moduleSpriteBatchClear(lua_State *L) { - assertNotNull(L, "Lua state is null"); - - spriteBatchClear(); - return 0; -} - -int moduleSpriteBatchPush(lua_State *L) { - assertNotNull(L, "Lua state is null"); - - // MinX, MinY, MaxX, MaxY - if( - !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || - !lua_isnumber(L, 4) - ) { - return luaL_error(L, "Sprite coordinates must be numbers"); - } - - // Color (struct) or nil for white - color_t *color = NULL; - if(lua_gettop(L) < 5 || lua_isnil(L, 5)) { - // Allow NULL - } else if(lua_isuserdata(L, 5)) { - color = (color_t*)luaL_checkudata(L, 5, "color_mt"); - } else { - return luaL_error(L, "Sprite color must be a color struct or nil"); - } - - // Optional UV min and maxes, defaults to 0,0 -> 1,1 - float_t u0 = 0.0f; - float_t v0 = 0.0f; - float_t u1 = 1.0f; - float_t v1 = 1.0f; - - if(lua_gettop(L) >= 7) { - if(!lua_isnumber(L, 6) || !lua_isnumber(L, 7)) { - return luaL_error(L, "Sprite UV min coordinates must be numbers"); - } - - u0 = (float_t)lua_tonumber(L, 6); - v0 = (float_t)lua_tonumber(L, 7); - } - - if(lua_gettop(L) >= 9) { - if(!lua_isnumber(L, 8) || !lua_isnumber(L, 9)) { - return luaL_error(L, "Sprite UV max coordinates must be numbers"); - } - - u1 = (float_t)lua_tonumber(L, 8); - v1 = (float_t)lua_tonumber(L, 9); - } - - float_t minX = (float_t)lua_tonumber(L, 1); - float_t minY = (float_t)lua_tonumber(L, 2); - float_t maxX = (float_t)lua_tonumber(L, 3); - float_t maxY = (float_t)lua_tonumber(L, 4); - - errorret_t ret = spriteBatchPush( - minX, - minY, - maxX, - maxY, - #if MESH_ENABLE_COLOR - color == NULL ? COLOR_WHITE : *color, - #endif - u0, - v0, - u1, - v1 - ); - if(ret.code != ERROR_OK) { - int err = luaL_error(L, - "Failed to push sprite to batch: %s", - ret.state->message - ); - errorCatch(errorPrint(ret)); - return err; - } - - return 0; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/modulespritebatch.h b/src/dusk/script/module/display/modulespritebatch.h index 85c6d284..b457b503 100644 --- a/src/dusk/script/module/display/modulespritebatch.h +++ b/src/dusk/script/module/display/modulespritebatch.h @@ -1,40 +1,71 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "display/spritebatch/spritebatch.h" +#include "script/module/math/modulevec2.h" +#include "script/module/math/modulevec4.h" -/** - * Register sprite batch functions to the given script context. - * - * @param context The script context to register sprite batch functions to. - */ -void moduleSpriteBatch(scriptcontext_t *context); +static int moduleSpriteBatchFlush(lua_State *L) { + assertNotNull(L, "Lua state is null"); + spriteBatchFlush(); + return 0; +} -/** - * Script binding for flushing the sprite batch. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleSpriteBatchFlush(lua_State *L); +static int moduleSpriteBatchClear(lua_State *L) { + assertNotNull(L, "Lua state is null"); + spriteBatchClear(); + return 0; +} -/** - * Script binding for clearing the sprite batch. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleSpriteBatchClear(lua_State *L); +static int moduleSpriteBatchPush(lua_State *L) { + assertNotNull(L, "Lua state is null"); -/** - * Script binding for pushing a sprite to the sprite batch. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleSpriteBatchPush(lua_State *L); \ No newline at end of file + vec2 min, max; + luaVec2Check(L, 1, min); + luaVec2Check(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"); + } + + 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; luaVec4Check(L, 4, uv); + u0 = uv[0]; v0 = uv[1]; u1 = uv[2]; v1 = uv[3]; + } + + errorret_t ret = spriteBatchPush( + min[0], min[1], max[0], max[1], + #if MESH_ENABLE_COLOR + color == NULL ? COLOR_WHITE : *color, + #endif + u0, v0, u1, v1 + ); + if(ret.code != ERROR_OK) { + int err = luaL_error(L, + "Failed to push sprite to batch: %s", + ret.state->message + ); + errorCatch(errorPrint(ret)); + return err; + } + + return 0; +} + +static void moduleSpriteBatch(lua_State *L) { + lua_register(L, "spriteBatchFlush", moduleSpriteBatchFlush); + lua_register(L, "spriteBatchClear", moduleSpriteBatchClear); + lua_register(L, "spriteBatchPush", moduleSpriteBatchPush); +} diff --git a/src/dusk/script/module/display/moduletext.c b/src/dusk/script/module/display/moduletext.c deleted file mode 100644 index de18aecc..00000000 --- a/src/dusk/script/module/display/moduletext.c +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletext.h" -#include "assert/assert.h" -#include "display/text/text.h" -#include "display/spritebatch/spritebatch.h" - -void moduleText(scriptcontext_t *context) { - assertNotNull(context, "Script context is null"); - - lua_register(context->luaState, "textDraw", moduleTextDraw); - lua_register(context->luaState, "textMeasure", moduleTextMeasure); -} - -int moduleTextDraw(lua_State *L) { - assertNotNull(L, "Lua state is null"); - - // Position. - if(!lua_isnumber(L, 1)) { - return luaL_error(L, "X position must be a number"); - } - if(!lua_isnumber(L, 2)) { - return luaL_error(L, "Y position must be a number"); - } - - const float_t x = (float_t)lua_tonumber(L, 1); - const float_t y = (float_t)lua_tonumber(L, 2); - - // String - if(!lua_isstring(L, 3)) { - return luaL_error(L, "Text to draw must be a string"); - } - const char_t *text = (const char_t*)lua_tostring(L, 3); - - // Optional color - color_t *color = NULL; - if(lua_gettop(L) < 4 || lua_isnil(L, 4)) { - // Allow NULL - } else if(lua_isuserdata(L, 4)) { - color = (color_t*)luaL_checkudata(L, 4, "color_mt"); - } else { - return luaL_error(L, "Sprite color must be a color struct or nil"); - } - - // For now, use the default font tileset and texture. - errorret_t ret = textDraw( - x, - y, - text, - color == NULL ? COLOR_WHITE : *color, - &DEFAULT_FONT_TILESET, - &DEFAULT_FONT_TEXTURE - ); - if(ret.code != ERROR_OK) { - errorCatch(errorPrint(ret)); - luaL_error(L, "Failed to draw text"); - } - - ret = spriteBatchFlush(); - if(ret.code != ERROR_OK) { - errorCatch(errorPrint(ret)); - luaL_error(L, "Failed to flush sprite batch after drawing text"); - } -} - -int moduleTextMeasure(lua_State *L) { - assertNotNull(L, "Lua state is null"); - - // String - if(!lua_isstring(L, 1)) { - return luaL_error(L, "Text to measure must be a string"); - } - const char_t *text = (const char_t*)lua_tostring(L, 1); - - int32_t width = 0; - int32_t height = 0; - textMeasure( - text, - &DEFAULT_FONT_TILESET, - &width, - &height - ); - - lua_pushnumber(L, width); - lua_pushnumber(L, height); - return 2; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/moduletext.h b/src/dusk/script/module/display/moduletext.h index 680e9b3f..03b39f7e 100644 --- a/src/dusk/script/module/display/moduletext.h +++ b/src/dusk/script/module/display/moduletext.h @@ -1,32 +1,72 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "display/text/text.h" +#include "display/spritebatch/spritebatch.h" +#include "script/module/math/modulevec2.h" -/** - * Register text rendering functions to the given script context. - * - * @param context The script context to register text functions to. - */ -void moduleText(scriptcontext_t *context); +static int moduleTextDraw(lua_State *L) { + assertNotNull(L, "Lua state is null"); -/** - * Script binding for drawing text. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleTextDraw(lua_State *L); + vec2 pos; luaVec2Check(L, 1, pos); -/** - * Script binding for measuring text dimensions. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleTextMeasure(lua_State *L); \ No newline at end of file + if(!lua_isstring(L, 2)) { + return luaL_error(L, "Text to draw must be a string"); + } + 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"); + } + + errorret_t ret = textDraw( + pos[0], pos[1], text, + color == NULL ? COLOR_WHITE : *color, + &DEFAULT_FONT_TILESET, + &DEFAULT_FONT_TEXTURE + ); + if(ret.code != ERROR_OK) { + errorCatch(errorPrint(ret)); + luaL_error(L, "Failed to draw text"); + } + + ret = spriteBatchFlush(); + if(ret.code != ERROR_OK) { + errorCatch(errorPrint(ret)); + luaL_error(L, "Failed to flush sprite batch after drawing text"); + } + return 0; +} + +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"); + } + const char_t *text = (const char_t *)lua_tostring(L, 1); + + int32_t w = 0, h = 0; + textMeasure(text, &DEFAULT_FONT_TILESET, &w, &h); + + vec2 size = { (float_t)w, (float_t)h }; + luaVec2Push(L, size); + return 1; +} + +static void moduleText(lua_State *L) { + assertNotNull(L, "Lua state is null"); + lua_register(L, "textDraw", moduleTextDraw); + lua_register(L, "textMeasure", moduleTextMeasure); +} diff --git a/src/dusk/script/module/display/moduletexture.c b/src/dusk/script/module/display/moduletexture.c deleted file mode 100644 index b819b292..00000000 --- a/src/dusk/script/module/display/moduletexture.c +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletexture.h" -#include "assert/assert.h" -#include "display/texture/texture.h" -#include "asset/loader/display/assettextureloader.h" -#include "util/memory.h" -#include "util/string.h" - -void moduleTexture(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be null"); - - lua_State *l = ctx->luaState; - assertNotNull(l, "Lua state cannot be null"); - - // Create metatable for texture structure. - if(luaL_newmetatable(l, "texture_mt") == 1) { - lua_pushcfunction(l, moduleTextureIndex); - lua_setfield(l, -2, "__index"); - - lua_pushcfunction(l, moduleTextureToString); - lua_setfield(l, -2, "__tostring"); - - lua_pushcfunction(l, moduleTextureGC); - lua_setfield(l, -2, "__gc"); - } - - // Texture formats - lua_pushnumber(l, TEXTURE_FORMAT_RGBA); - lua_setglobal(l, "TEXTURE_FORMAT_RGBA"); - - lua_register(ctx->luaState, "textureLoad", moduleTextureLoad); -} - -int moduleTextureIndex(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt"); - assertNotNull(tex, "Texture pointer cannot be NULL."); - - const char *key = luaL_checkstring(l, 2); - assertNotNull(key, "Key cannot be NULL."); - - if(stringCompare(key, "width") == 0) { - lua_pushnumber(l, tex->width); - return 1; - } else if(stringCompare(key, "height") == 0) { - lua_pushnumber(l, tex->height); - return 1; - } - - lua_pushnil(l); - return 1; -} - -int moduleTextureToString(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt"); - assertNotNull(tex, "Texture pointer cannot be NULL."); - - char buffer[64]; - snprintf(buffer, sizeof(buffer), "Texture(%dx%d)", tex->width, tex->height); - lua_pushstring(l, buffer); - return 1; -} - -int moduleTextureGC(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt"); - assertNotNull(tex, "Texture pointer cannot be NULL."); - - textureDispose(tex); - return 0; -} - -int moduleTextureLoad(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - if(!lua_isstring(l, 1)) { - luaL_error(l, "First argument must be a string filename."); - return 0; - } - - if(!lua_isnumber(l, 2)) { - luaL_error(l, "Second argument must be a number format."); - return 0; - } - - const char_t *filename = lua_tostring(l, 1); - assertNotNull(filename, "Filename cannot be NULL."); - assertStrLenMin(filename, 1, "Filename cannot be empty."); - - // Create texture owned to lua - texture_t *tex = (texture_t *)lua_newuserdata(l, sizeof(texture_t)); - memoryZero(tex, sizeof(texture_t)); - - textureformat_t format = (textureformat_t)lua_tonumber(l, 2); - - errorret_t ret = assetTextureLoad(filename, tex, format); - if(ret.code != ERROR_OK) { - errorCatch(errorPrint(ret)); - luaL_error(l, "Failed to load texture asset: %s", filename); - return 0; - } - - // Set metatable - luaL_getmetatable(l, "texture_mt"); - lua_setmetatable(l, -2); - - // Return the texture - return 1; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/moduletexture.h b/src/dusk/script/module/display/moduletexture.h index 5d8e1796..4764d644 100644 --- a/src/dusk/script/module/display/moduletexture.h +++ b/src/dusk/script/module/display/moduletexture.h @@ -1,15 +1,98 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "display/texture/texture.h" +#include "asset/loader/display/assettextureloader.h" -void moduleTexture(scriptcontext_t *ctx); -int moduleTextureIndex(lua_State *l); -int moduleTextureToString(lua_State *l); -int moduleTextureGC(lua_State *l); -int moduleTextureLoad(lua_State *l); \ No newline at end of file +static int moduleTextureIndex(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL."); + + texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt"); + assertNotNull(tex, "Texture pointer cannot be NULL."); + const char *key = luaL_checkstring(l, 2); + assertNotNull(key, "Key cannot be NULL."); + + if(stringCompare(key, "width") == 0) { + lua_pushnumber(l, tex->width); return 1; + } else if(stringCompare(key, "height") == 0) { + lua_pushnumber(l, tex->height); return 1; + } + + lua_pushnil(l); + return 1; +} + +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; +} + +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; +} + +static int moduleTextureLoad(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL."); + + if(!lua_isstring(l, 1)) { + luaL_error(l, "First argument must be a string filename."); + return 0; + } + if(!lua_isnumber(l, 2)) { + luaL_error(l, "Second argument must be a number format."); + return 0; + } + + const char_t *filename = lua_tostring(l, 1); + assertNotNull(filename, "Filename cannot be NULL."); + assertStrLenMin(filename, 1, "Filename cannot be empty."); + + texture_t *tex = (texture_t *)lua_newuserdata(l, sizeof(texture_t)); + memoryZero(tex, sizeof(texture_t)); + + textureformat_t format = (textureformat_t)lua_tonumber(l, 2); + errorret_t ret = assetTextureLoad(filename, tex, format); + if(ret.code != ERROR_OK) { + errorCatch(errorPrint(ret)); + luaL_error(l, "Failed to load texture asset: %s", filename); + return 0; + } + + luaL_getmetatable(l, "texture_mt"); + lua_setmetatable(l, -2); + return 1; +} + +static void moduleTexture(lua_State *L) { + assertNotNull(L, "Lua state cannot be null"); + + 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); +} diff --git a/src/dusk/script/module/display/moduletileset.c b/src/dusk/script/module/display/moduletileset.c deleted file mode 100644 index de13430b..00000000 --- a/src/dusk/script/module/display/moduletileset.c +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletileset.h" -#include "assert/assert.h" -#include "display/texture/tileset.h" -#include "util/memory.h" -#include "util/string.h" -#include "asset/loader/display/assettilesetloader.h" - -void moduleTileset(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - // Tileset metatable - if(luaL_newmetatable(ctx->luaState, "tileset_mt")) { - lua_pushcfunction(ctx->luaState, moduleTilesetIndex); - lua_setfield(ctx->luaState, -2, "__index"); - - lua_pushcfunction(ctx->luaState, moduleTilesetToString); - lua_setfield(ctx->luaState, -2, "__tostring"); - } - lua_pop(ctx->luaState, 1); // Pop the metatable - - lua_register(ctx->luaState, "tilesetLoad", moduleTilesetLoad); - lua_register(ctx->luaState, "tilesetTileGetUV", moduleTilesetTileGetUV); - lua_register( - ctx->luaState, "tilesetPositionGetUV", moduleTilesetPositionGetUV - ); -} - -int moduleTilesetIndex(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = luaL_checkstring(l, 2); - assertNotNull(key, "Key cannot be NULL."); - - tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt"); - assertNotNull(ts, "Tileset pointer cannot be NULL."); - - if(stringCompare(key, "tileWidth") == 0) { - lua_pushnumber(l, ts->tileWidth); - return 1; - } else if(stringCompare(key, "tileHeight") == 0) { - lua_pushnumber(l, ts->tileHeight); - return 1; - } else if(stringCompare(key, "tileCount") == 0) { - lua_pushnumber(l, ts->tileCount); - return 1; - } else if(stringCompare(key, "columns") == 0) { - lua_pushnumber(l, ts->columns); - return 1; - } else if(stringCompare(key, "rows") == 0) { - lua_pushnumber(l, ts->rows); - return 1; - } - - lua_pushnil(l); - return 1; -} - -int moduleTilesetToString(lua_State *l) { - tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt"); - assertNotNull(ts, "Tileset pointer cannot be NULL."); - - lua_pushfstring(l, "Tileset: %dx%d tile, %d columns, %d rows", - ts->tileWidth, ts->tileHeight, ts->columns, ts->rows - ); - return 1; -} - -int moduleTilesetTileGetUV(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - if(!lua_isuserdata(l, 1)) { - luaL_error(l, "First argument must be a tileset userdata."); - return 0; - } - - tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt"); - assertNotNull(ts, "Tileset pointer cannot be NULL."); - - if(!lua_isnumber(l, 2)) { - luaL_error(l, "Second arguments must be tile index."); - return 0; - } - uint16_t tileIndex = (uint16_t)lua_tonumber(l, 2); - - // Create vec4 that lua owns - vec4 *uv = (vec4 *)lua_newuserdata(l, sizeof(vec4)); - tilesetTileGetUV(ts, tileIndex, *uv); - - // Set metatable - luaL_getmetatable(l, "vec4_mt"); - lua_setmetatable(l, -2); - return 1; -} - -int moduleTilesetPositionGetUV(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - if(!lua_isuserdata(l, 1)) { - luaL_error(l, "First argument must be a tileset userdata."); - return 0; - } - - tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt"); - assertNotNull(ts, "Tileset pointer cannot be NULL."); - - if(!lua_isnumber(l, 2)) { - luaL_error(l, "Second arguments must be column number."); - return 0; - } - uint16_t column = (uint16_t)lua_tonumber(l, 2); - - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Third arguments must be row number."); - return 0; - } - uint16_t row = (uint16_t)lua_tonumber(l, 3); - - // Create vec4 that lua owns - vec4 *uv = (vec4 *)lua_newuserdata(l, sizeof(vec4)); - tilesetPositionGetUV(ts, column, row, *uv); - - // Set metatable - luaL_getmetatable(l, "vec4_mt"); - lua_setmetatable(l, -2); - return 1; -} - -int moduleTilesetLoad(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - if(!lua_isstring(l, 1)) { - luaL_error(l, "First argument must be a string filename."); - return 0; - } - - const char_t *filename = lua_tostring(l, 1); - assertNotNull(filename, "Filename cannot be NULL."); - assertStrLenMin(filename, 1, "Filename cannot be empty."); - - // Create texture owned to lua - tileset_t *tileset = (tileset_t *)lua_newuserdata(l, sizeof(tileset_t)); - memoryZero(tileset, sizeof(tileset_t)); - - errorret_t ret = assetTilesetLoad(filename, tileset); - if(ret.code != ERROR_OK) { - errorCatch(errorPrint(ret)); - luaL_error(l, "Failed to load tileset asset: %s", filename); - return 0; - } - - // Set metatable - luaL_getmetatable(l, "tileset_mt"); - lua_setmetatable(l, -2); - - // Return the tileset - return 1; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/moduletileset.h b/src/dusk/script/module/display/moduletileset.h index 5489e089..fd9f4b08 100644 --- a/src/dusk/script/module/display/moduletileset.h +++ b/src/dusk/script/module/display/moduletileset.h @@ -1,56 +1,108 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "display/texture/tileset.h" +#include "asset/loader/display/assettilesetloader.h" +#include "script/module/math/modulevec4.h" -/** - * Registers the tileset module in the scripting context. - * - * @param ctx The scripting context to register the module in. - */ -void moduleTileset(scriptcontext_t *ctx); +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."); -/** - * __index metamethod for tileset userdata. - * - * @param l The Lua state. - * @return The number of return values on the Lua stack. - */ -int moduleTilesetIndex(lua_State *l); + 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; + } -/** - * __tostring metamethod for tileset userdata. - * - * @param l The Lua state. - * @return The number of return values on the Lua stack. - */ -int moduleTilesetToString(lua_State *l); + lua_pushnil(l); + return 1; +} -/** - * Lua function to get the UV coordinates for a tile index in a tileset. - * - * @param l The Lua state. - * @return The number of return values on the Lua stack. - */ -int moduleTilesetTileGetUV(lua_State *l); +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 + ); + return 1; +} -/** - * Lua function to get the UV coordinates for a tile position in a tileset. - * - * @param l The Lua state. - * @return The number of return values on the Lua stack. - */ -int moduleTilesetPositionGetUV(lua_State *l); +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); + luaVec4Push(l, uv); + return 1; +} -/** - * Lua function to load a tileset from a texture and tile dimensions. - * - * @param l The Lua state. - * @return The number of return values on the Lua stack. - */ -int moduleTilesetLoad(lua_State *l); \ No newline at end of file +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); + luaVec4Push(l, uv); + return 1; +} + +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; + } + + const char_t *filename = lua_tostring(l, 1); + assertNotNull(filename, "Filename cannot be NULL."); + assertStrLenMin(filename, 1, "Filename cannot be empty."); + + tileset_t *tileset = (tileset_t *)lua_newuserdata(l, sizeof(tileset_t)); + memoryZero(tileset, sizeof(tileset_t)); + + errorret_t ret = assetTilesetLoad(filename, tileset); + if(ret.code != ERROR_OK) { + errorCatch(errorPrint(ret)); + luaL_error(l, "Failed to load tileset asset: %s", filename); + return 0; + } + + luaL_getmetatable(l, "tileset_mt"); + lua_setmetatable(l, -2); + return 1; +} + +static void moduleTileset(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + 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); + + lua_register(L, "tilesetLoad", moduleTilesetLoad); + lua_register(L, "tilesetTileGetUV", moduleTilesetTileGetUV); + lua_register(L, "tilesetPositionGetUV", moduleTilesetPositionGetUV); +} diff --git a/src/dusk/script/module/entity/CMakeLists.txt b/src/dusk/script/module/entity/CMakeLists.txt deleted file mode 100644 index 5a511cb0..00000000 --- a/src/dusk/script/module/entity/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleentity.c -) - -# Subdirectories -add_subdirectory(display) -add_subdirectory(physics) -add_subdirectory(script) diff --git a/src/dusk/script/module/entity/display/CMakeLists.txt b/src/dusk/script/module/entity/display/CMakeLists.txt deleted file mode 100644 index 8b90f2f8..00000000 --- a/src/dusk/script/module/entity/display/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleentityposition.c - moduleentitycamera.c - moduleentitymesh.c - moduleentitymaterial.c -) diff --git a/src/dusk/script/module/entity/display/moduleentitycamera.c b/src/dusk/script/module/entity/display/moduleentitycamera.c deleted file mode 100644 index 4c1e10d5..00000000 --- a/src/dusk/script/module/entity/display/moduleentitycamera.c +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentitycamera.h" -#include "assert/assert.h" -#include "entity/entity.h" -#include "entity/component/display/entitycamera.h" - -void moduleEntityCamera(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - #define REG(name, func) lua_register(ctx->luaState, name, func) - REG("entityCameraAdd", moduleEntityCameraAdd); - REG("entityCameraGetZNear", moduleEntityCameraGetZNear); - REG("entityCameraSetZNear", moduleEntityCameraSetZNear); - REG("entityCameraGetZFar", moduleEntityCameraGetZFar); - REG("entityCameraSetZFar", moduleEntityCameraSetZFar); - #undef REG -} - -int moduleEntityCameraAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_CAMERA); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} - -int moduleEntityCameraGetZNear(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - lua_pushnumber(L, (lua_Number)entityCameraGetZNear(entityId, compId)); - return 1; -} - -int moduleEntityCameraSetZNear(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - float_t zNear = (float_t)luaL_checknumber(L, 3); - entityCameraSetZNear(entityId, compId, zNear); - return 0; -} - -int moduleEntityCameraGetZFar(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - lua_pushnumber(L, (lua_Number)entityCameraGetZFar(entityId, compId)); - return 1; -} - -int moduleEntityCameraSetZFar(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - float_t zFar = (float_t)luaL_checknumber(L, 3); - entityCameraSetZFar(entityId, compId, zFar); - return 0; -} diff --git a/src/dusk/script/module/entity/display/moduleentitycamera.h b/src/dusk/script/module/entity/display/moduleentitycamera.h deleted file mode 100644 index d13223db..00000000 --- a/src/dusk/script/module/entity/display/moduleentitycamera.h +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -void moduleEntityCamera(scriptcontext_t *ctx); - -int moduleEntityCameraAdd(lua_State *L); -int moduleEntityCameraGetZNear(lua_State *L); -int moduleEntityCameraSetZNear(lua_State *L); -int moduleEntityCameraGetZFar(lua_State *L); -int moduleEntityCameraSetZFar(lua_State *L); diff --git a/src/dusk/script/module/entity/display/moduleentitymaterial.c b/src/dusk/script/module/entity/display/moduleentitymaterial.c deleted file mode 100644 index 8368b38a..00000000 --- a/src/dusk/script/module/entity/display/moduleentitymaterial.c +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentitymaterial.h" -#include "assert/assert.h" -#include "entity/entity.h" -#include "entity/component/display/entitymaterial.h" - -void moduleEntityMaterial(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - lua_register(ctx->luaState, "entityMaterialAdd", moduleEntityMaterialAdd); -} - -int moduleEntityMaterialAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_MATERIAL); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} diff --git a/src/dusk/script/module/entity/display/moduleentitymaterial.h b/src/dusk/script/module/entity/display/moduleentitymaterial.h deleted file mode 100644 index beccb2ad..00000000 --- a/src/dusk/script/module/entity/display/moduleentitymaterial.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -void moduleEntityMaterial(scriptcontext_t *ctx); - -int moduleEntityMaterialAdd(lua_State *L); diff --git a/src/dusk/script/module/entity/display/moduleentitymesh.c b/src/dusk/script/module/entity/display/moduleentitymesh.c deleted file mode 100644 index ebb4b731..00000000 --- a/src/dusk/script/module/entity/display/moduleentitymesh.c +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentitymesh.h" -#include "assert/assert.h" -#include "entity/entity.h" -#include "entity/component/display/entitymesh.h" - -void moduleEntityMesh(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - lua_register(ctx->luaState, "entityMeshAdd", moduleEntityMeshAdd); -} - -int moduleEntityMeshAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_MESH); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} diff --git a/src/dusk/script/module/entity/display/moduleentityposition.c b/src/dusk/script/module/entity/display/moduleentityposition.c deleted file mode 100644 index 0bbdee6d..00000000 --- a/src/dusk/script/module/entity/display/moduleentityposition.c +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentityposition.h" -#include "assert/assert.h" -#include "entity/entity.h" -#include "entity/component/display/entityposition.h" - -void moduleEntityPosition(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - #define REG(name, func) lua_register(ctx->luaState, name, func) - REG("entityPositionAdd", moduleEntityPositionAdd); - REG("entityPositionSetPosition", moduleEntityPositionSetPosition); - REG("entityPositionGetPosition", moduleEntityPositionGetPosition); - REG("entityPositionSetRotation", moduleEntityPositionSetRotation); - REG("entityPositionGetRotation", moduleEntityPositionGetRotation); - REG("entityPositionSetScale", moduleEntityPositionSetScale); - REG("entityPositionGetScale", moduleEntityPositionGetScale); - REG("entityPositionLookAt", moduleEntityPositionLookAt); - #undef REG -} - -int moduleEntityPositionAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_POSITION); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} - -int moduleEntityPositionSetPosition(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 pos = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPositionSetPosition(entityId, compId, pos); - return 0; -} - -int moduleEntityPositionGetPosition(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 pos; - entityPositionGetPosition(entityId, compId, pos); - lua_pushnumber(L, pos[0]); - lua_pushnumber(L, pos[1]); - lua_pushnumber(L, pos[2]); - return 3; -} - -int moduleEntityPositionSetRotation(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 rot = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPositionSetRotation(entityId, compId, rot); - return 0; -} - -int moduleEntityPositionGetRotation(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 rot; - entityPositionGetRotation(entityId, compId, rot); - lua_pushnumber(L, rot[0]); - lua_pushnumber(L, rot[1]); - lua_pushnumber(L, rot[2]); - return 3; -} - -int moduleEntityPositionSetScale(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 scale = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPositionSetScale(entityId, compId, scale); - return 0; -} - -int moduleEntityPositionGetScale(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 scale; - entityPositionGetScale(entityId, compId, scale); - lua_pushnumber(L, scale[0]); - lua_pushnumber(L, scale[1]); - lua_pushnumber(L, scale[2]); - return 3; -} - -int moduleEntityPositionLookAt(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 target = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - vec3 up = { - (float_t)luaL_checknumber(L, 6), - (float_t)luaL_checknumber(L, 7), - (float_t)luaL_checknumber(L, 8) - }; - vec3 eye = { - (float_t)luaL_checknumber(L, 9), - (float_t)luaL_checknumber(L, 10), - (float_t)luaL_checknumber(L, 11) - }; - entityPositionLookAt(entityId, compId, target, up, eye); - return 0; -} diff --git a/src/dusk/script/module/entity/display/moduleentityposition.h b/src/dusk/script/module/entity/display/moduleentityposition.h deleted file mode 100644 index 0ab13d41..00000000 --- a/src/dusk/script/module/entity/display/moduleentityposition.h +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -void moduleEntityPosition(scriptcontext_t *ctx); - -int moduleEntityPositionAdd(lua_State *L); -int moduleEntityPositionSetPosition(lua_State *L); -int moduleEntityPositionGetPosition(lua_State *L); -int moduleEntityPositionSetRotation(lua_State *L); -int moduleEntityPositionGetRotation(lua_State *L); -int moduleEntityPositionSetScale(lua_State *L); -int moduleEntityPositionGetScale(lua_State *L); -int moduleEntityPositionLookAt(lua_State *L); diff --git a/src/dusk/script/module/entity/moduleentity.c b/src/dusk/script/module/entity/moduleentity.c deleted file mode 100644 index 82a8b3f3..00000000 --- a/src/dusk/script/module/entity/moduleentity.c +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentity.h" -#include "assert/assert.h" -#include "entity/entitymanager.h" -#include "entity/entity.h" -#include "display/moduleentityposition.h" -#include "display/moduleentitycamera.h" -#include "display/moduleentitymesh.h" -#include "display/moduleentitymaterial.h" -#include "physics/moduleentityphysics.h" -#include "script/moduleentityscript.h" - -void moduleEntity(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - lua_register(ctx->luaState, "entityAdd", moduleEntityAdd); - lua_register(ctx->luaState, "entityRemove", moduleEntityRemove); - - moduleEntityPosition(ctx); - moduleEntityCamera(ctx); - moduleEntityMesh(ctx); - moduleEntityMaterial(ctx); - moduleEntityPhysics(ctx); - moduleEntityScript(ctx); -} - -int moduleEntityAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t id = entityManagerAdd(); - lua_pushnumber(L, (lua_Number)id); - return 1; -} - -int moduleEntityRemove(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - if(!lua_isnumber(L, 1)) { - luaL_error(L, "entityRemove requires a number entity ID"); - return 0; - } - - entityid_t id = (entityid_t)luaL_checknumber(L, 1); - entityDispose(id); - return 0; -} diff --git a/src/dusk/script/module/entity/moduleentity.h b/src/dusk/script/module/entity/moduleentity.h index 3f7da12f..c7ecde2e 100644 --- a/src/dusk/script/module/entity/moduleentity.h +++ b/src/dusk/script/module/entity/moduleentity.h @@ -6,27 +6,337 @@ */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "entity/entity.h" +#include "entity/entitymanager.h" +#include "entity/component/display/entityposition.h" +#include "entity/component/display/entitycamera.h" +#include "entity/component/display/entitymesh.h" +#include "entity/component/display/entitymaterial.h" -/** - * Register the entity module within the given script context. - * - * @param ctx The script context to register the module in. - */ -void moduleEntity(scriptcontext_t *ctx); +// ============================================================================ +// Handles +// ============================================================================ -/** - * Lua binding for entityManagerAdd - creates a new entity and returns its ID. - * - * @param L The Lua state. - * @return int Number of return values on the Lua stack. - */ -int moduleEntityAdd(lua_State *L); +typedef struct { entityid_t entityId; componentid_t compId; } entitypos_handle_t; +typedef struct { entityid_t entityId; componentid_t compId; } entitycam_handle_t; +typedef struct { entityid_t entityId; componentid_t compId; } entitymesh_handle_t; +typedef struct { entityid_t entityId; componentid_t compId; } entitymat_handle_t; -/** - * Lua binding for entityDispose - disposes of an entity by its ID. - * - * @param L The Lua state. - * @return int Number of return values on the Lua stack. - */ -int moduleEntityRemove(lua_State *L); +// ============================================================================ +// entityPosition +// ============================================================================ + +static int _posIndex(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 int _posNewindex(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); + 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; + } + luaL_error(L, "entitypos: unknown property '%s'", k); + return 0; +} + +static int _posLookAt(lua_State *L) { + entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt"); + vec3 target = { + (float_t)luaL_checknumber(L, 2), + (float_t)luaL_checknumber(L, 3), + (float_t)luaL_checknumber(L, 4) + }; + 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); + } + vec3 eye; + entityPositionGetPosition(h->entityId, h->compId, eye); + entityPositionLookAt(h->entityId, h->compId, target, up, eye); + return 0; +} + +static int _entityPositionAdd(lua_State *L) { + entityid_t id = (entityid_t)luaL_checknumber(L, 1); + 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; +} + +// ============================================================================ +// entityCamera +// ============================================================================ + +static int _camIndex(lua_State *L) { + entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt"); + const char *k = luaL_checkstring(L, 2); + 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; +} + +static int _camNewindex(lua_State *L) { + entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt"); + const char *k = luaL_checkstring(L, 2); + 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; +} + +static int _entityCameraAdd(lua_State *L) { + entityid_t id = (entityid_t)luaL_checknumber(L, 1); + componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_CAMERA); + entitycam_handle_t *h = lua_newuserdata(L, sizeof(entitycam_handle_t)); + h->entityId = id; h->compId = comp; + luaL_getmetatable(L, "entitycam_mt"); lua_setmetatable(L, -2); + return 1; +} + +// ============================================================================ +// entityMesh +// ============================================================================ + +static int _meshIndex(lua_State *L) { + lua_getmetatable(L, 1); lua_getfield(L, -1, luaL_checkstring(L, 2)); return 1; +} + +static int _meshGeneratePlane(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; +} + +static int _meshGenerateCapsule(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; +} + +static int _entityMeshAdd(lua_State *L) { + entityid_t id = (entityid_t)luaL_checknumber(L, 1); + 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; +} + +// ============================================================================ +// entityMaterial +// ============================================================================ + +static int _matIndex(lua_State *L) { + lua_getmetatable(L, 1); lua_getfield(L, -1, luaL_checkstring(L, 2)); return 1; +} + +static int _matNewindex(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; +} + +static int _entityMaterialAdd(lua_State *L) { + entityid_t id = (entityid_t)luaL_checknumber(L, 1); + 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; +} + +// ============================================================================ +// Component type Lua constants (auto-generated from componentlist.h) +// ============================================================================ + +#define X(enumName, type, field, init, dispose) \ + "COMPONENT_TYPE_" #enumName " = \"" #field "\"\n" +static const char_t *COMPONENT_TYPE_SCRIPT = +#include "entity/componentlist.h" +; +#undef X + +// ============================================================================ +// Entity base class Lua script +// ============================================================================ + +static const char_t *ENTITY_SCRIPT = + "Entity = {}\n" + "Entity.__index = Entity\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" + "\n" + "local _addFns = {\n" + " [COMPONENT_TYPE_POSITION] = entityPositionAdd,\n" + " [COMPONENT_TYPE_CAMERA] = entityCameraAdd,\n" + " [COMPONENT_TYPE_MESH] = entityMeshAdd,\n" + " [COMPONENT_TYPE_MATERIAL] = entityMaterialAdd,\n" + "}\n" + "\n" + "function Entity.new()\n" + " return setmetatable({ id = entityAdd() }, Entity)\n" + "end\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" +; + +// ============================================================================ +// entityAdd / entityRemove +// ============================================================================ + +static int _entityAdd(lua_State *L) { + lua_pushnumber(L, (lua_Number)entityManagerAdd()); + return 1; +} + +static int _entityRemove(lua_State *L) { + entityDispose((entityid_t)luaL_checknumber(L, 1)); + return 0; +} + +// ============================================================================ +// Register +// ============================================================================ + +static void moduleEntity(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + luaL_newmetatable(L, "entitypos_mt"); + lua_pushcfunction(L, _posIndex); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, _posNewindex); lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, _posLookAt); lua_setfield(L, -2, "lookAt"); + lua_pop(L, 1); + + luaL_newmetatable(L, "entitycam_mt"); + lua_pushcfunction(L, _camIndex); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, _camNewindex); lua_setfield(L, -2, "__newindex"); + lua_pop(L, 1); + + luaL_newmetatable(L, "entitymesh_mt"); + lua_pushcfunction(L, _meshIndex); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, _meshGeneratePlane); lua_setfield(L, -2, "generatePlane"); + lua_pushcfunction(L, _meshGenerateCapsule); lua_setfield(L, -2, "generateCapsule"); + lua_pop(L, 1); + + luaL_newmetatable(L, "entitymat_mt"); + lua_pushcfunction(L, _matIndex); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, _matNewindex); lua_setfield(L, -2, "__newindex"); + lua_pop(L, 1); + + lua_register(L, "entityAdd", _entityAdd); + lua_register(L, "entityRemove", _entityRemove); + lua_register(L, "entityPositionAdd", _entityPositionAdd); + lua_register(L, "entityCameraAdd", _entityCameraAdd); + lua_register(L, "entityMeshAdd", _entityMeshAdd); + lua_register(L, "entityMaterialAdd", _entityMaterialAdd); + + luaL_dostring(L, COMPONENT_TYPE_SCRIPT); + luaL_dostring(L, ENTITY_SCRIPT); +} diff --git a/src/dusk/script/module/entity/physics/CMakeLists.txt b/src/dusk/script/module/entity/physics/CMakeLists.txt deleted file mode 100644 index ba860634..00000000 --- a/src/dusk/script/module/entity/physics/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleentityphysics.c -) diff --git a/src/dusk/script/module/entity/physics/moduleentityphysics.c b/src/dusk/script/module/entity/physics/moduleentityphysics.c deleted file mode 100644 index 00947360..00000000 --- a/src/dusk/script/module/entity/physics/moduleentityphysics.c +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentityphysics.h" -#include "assert/assert.h" -#include "entity/entity.h" -#include "entity/component/physics/entityphysics.h" - -void moduleEntityPhysics(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - #define REG(name, func) lua_register(ctx->luaState, name, func) - REG("entityPhysicsAdd", moduleEntityPhysicsAdd); - REG("entityPhysicsSetVelocity", moduleEntityPhysicsSetVelocity); - REG("entityPhysicsGetVelocity", moduleEntityPhysicsGetVelocity); - REG("entityPhysicsApplyImpulse", moduleEntityPhysicsApplyImpulse); - REG("entityPhysicsIsOnGround", moduleEntityPhysicsIsOnGround); - REG("entityPhysicsSetShapeCube", moduleEntityPhysicsSetShapeCube); - REG("entityPhysicsSetShapeSphere", moduleEntityPhysicsSetShapeSphere); - REG("entityPhysicsSetShapeCapsule", moduleEntityPhysicsSetShapeCapsule); - REG("entityPhysicsSetShapePlane", moduleEntityPhysicsSetShapePlane); - #undef REG -} - -int moduleEntityPhysicsAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_PHYSICS); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} - -int moduleEntityPhysicsSetVelocity(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 vel = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPhysicsSetVelocity(entityId, compId, vel); - return 0; -} - -int moduleEntityPhysicsGetVelocity(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 vel; - entityPhysicsGetVelocity(entityId, compId, vel); - lua_pushnumber(L, vel[0]); - lua_pushnumber(L, vel[1]); - lua_pushnumber(L, vel[2]); - return 3; -} - -int moduleEntityPhysicsApplyImpulse(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 impulse = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPhysicsApplyImpulse(entityId, compId, impulse); - return 0; -} - -int moduleEntityPhysicsIsOnGround(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - lua_pushboolean(L, (int)entityPhysicsIsOnGround(entityId, compId)); - return 1; -} - -int moduleEntityPhysicsSetShapeCube(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - physicsshape_t shape; - shape.type = PHYSICS_SHAPE_CUBE; - shape.data.cube.halfExtents[0] = (float_t)luaL_checknumber(L, 3); - shape.data.cube.halfExtents[1] = (float_t)luaL_checknumber(L, 4); - shape.data.cube.halfExtents[2] = (float_t)luaL_checknumber(L, 5); - entityPhysicsSetShape(entityId, compId, shape); - return 0; -} - -int moduleEntityPhysicsSetShapeSphere(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - physicsshape_t shape; - shape.type = PHYSICS_SHAPE_SPHERE; - shape.data.sphere.radius = (float_t)luaL_checknumber(L, 3); - entityPhysicsSetShape(entityId, compId, shape); - return 0; -} - -int moduleEntityPhysicsSetShapeCapsule(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - physicsshape_t shape; - shape.type = PHYSICS_SHAPE_CAPSULE; - shape.data.capsule.radius = (float_t)luaL_checknumber(L, 3); - shape.data.capsule.halfHeight = (float_t)luaL_checknumber(L, 4); - entityPhysicsSetShape(entityId, compId, shape); - return 0; -} - -int moduleEntityPhysicsSetShapePlane(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - physicsshape_t shape; - shape.type = PHYSICS_SHAPE_PLANE; - shape.data.plane.normal[0] = (float_t)luaL_checknumber(L, 3); - shape.data.plane.normal[1] = (float_t)luaL_checknumber(L, 4); - shape.data.plane.normal[2] = (float_t)luaL_checknumber(L, 5); - shape.data.plane.distance = (float_t)luaL_checknumber(L, 6); - entityPhysicsSetShape(entityId, compId, shape); - return 0; -} diff --git a/src/dusk/script/module/entity/physics/moduleentityphysics.h b/src/dusk/script/module/entity/physics/moduleentityphysics.h deleted file mode 100644 index 93175881..00000000 --- a/src/dusk/script/module/entity/physics/moduleentityphysics.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -void moduleEntityPhysics(scriptcontext_t *ctx); - -int moduleEntityPhysicsAdd(lua_State *L); -int moduleEntityPhysicsSetVelocity(lua_State *L); -int moduleEntityPhysicsGetVelocity(lua_State *L); -int moduleEntityPhysicsApplyImpulse(lua_State *L); -int moduleEntityPhysicsIsOnGround(lua_State *L); -int moduleEntityPhysicsSetShapeCube(lua_State *L); -int moduleEntityPhysicsSetShapeSphere(lua_State *L); -int moduleEntityPhysicsSetShapeCapsule(lua_State *L); -int moduleEntityPhysicsSetShapePlane(lua_State *L); diff --git a/src/dusk/script/module/entity/script/CMakeLists.txt b/src/dusk/script/module/entity/script/CMakeLists.txt deleted file mode 100644 index d1fa1a37..00000000 --- a/src/dusk/script/module/entity/script/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleentityscript.c -) diff --git a/src/dusk/script/module/entity/script/moduleentityscript.c b/src/dusk/script/module/entity/script/moduleentityscript.c deleted file mode 100644 index d9c85553..00000000 --- a/src/dusk/script/module/entity/script/moduleentityscript.c +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentityscript.h" -#include "assert/assert.h" -#include "entity/entity.h" -#include "entity/entitymanager.h" -#include "entity/component/script/entityscript.h" - -void moduleEntityScript(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - #define REG(name, func) lua_register(ctx->luaState, name, func) - REG("entityScriptAdd", moduleEntityScriptAdd); - REG("entityScriptSetScript", moduleEntityScriptSetScript); - REG("entityScriptUpdate", moduleEntityScriptUpdate); - REG("entityAddScripted", moduleEntityAddScripted); - #undef REG -} - -int moduleEntityScriptAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_ENTITYSCRIPT); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} - -int moduleEntityScriptSetScript(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - const char_t *filename = luaL_checkstring(L, 3); - - errorret_t err = entityScriptSetScript(entityId, compId, filename); - if(err.code != ERROR_OK) { - luaL_error(L, "Failed to set entity script: %s", filename); - errorCatch(errorPrint(err)); - return 0; - } - - return 0; -} - -int moduleEntityAddScripted(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - const char_t *filename = luaL_checkstring(L, 1); - - entityid_t entityId = entityManagerAdd(); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_ENTITYSCRIPT); - - errorret_t err = entityScriptSetScript(entityId, compId, filename); - if(err.code != ERROR_OK) { - luaL_error(L, "Failed to set entity script: %s", filename); - errorCatch(errorPrint(err)); - return 0; - } - - lua_pushnumber(L, (lua_Number)entityId); - lua_pushnumber(L, (lua_Number)compId); - return 2; -} - -int moduleEntityScriptUpdate(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - - errorret_t err = entityScriptUpdate(entityId, compId); - if(err.code != ERROR_OK) { - luaL_error(L, "Failed to update entity script"); - errorCatch(errorPrint(err)); - return 0; - } - - return 0; -} diff --git a/src/dusk/script/module/entity/script/moduleentityscript.h b/src/dusk/script/module/entity/script/moduleentityscript.h deleted file mode 100644 index 00ab45ab..00000000 --- a/src/dusk/script/module/entity/script/moduleentityscript.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -void moduleEntityScript(scriptcontext_t *ctx); - -int moduleEntityScriptAdd(lua_State *L); -int moduleEntityScriptSetScript(lua_State *L); -int moduleEntityScriptUpdate(lua_State *L); -int moduleEntityAddScripted(lua_State *L); diff --git a/src/dusk/script/module/event/CMakeLists.txt b/src/dusk/script/module/event/CMakeLists.txt deleted file mode 100644 index ffc4e91c..00000000 --- a/src/dusk/script/module/event/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2025 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleevent.c -) \ No newline at end of file diff --git a/src/dusk/script/module/event/moduleevent.c b/src/dusk/script/module/event/moduleevent.c deleted file mode 100644 index db4186aa..00000000 --- a/src/dusk/script/module/event/moduleevent.c +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleevent.h" -#include "event/event.h" -#include "engine/engine.h" -#include "assert/assert.h" - -int moduleEventSubscribe(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // State has user pointer to owning scriptcontext_t - scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L); - assertNotNull(context, "Script context cannot be NULL"); - - // Expecting event pointer - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "eventSubscribe: Expected event pointer as first argument"); - return 0; - } - - // Expecting callback function (Lua function) - if(!lua_isfunction(L, 2)) { - luaL_error(L, "eventSubscribe: Expected function as second argument"); - return 0; - } - - event_t *event = (event_t *)lua_touserdata(L, 1); - assertNotNull(event, "Event cannot be NULL"); - - eventsub_t id = eventSubscribeScriptContext(event, context, 2); - - // Pass back to lua. - lua_pushnumber(L, id); - return 1; -} - -void moduleEvent(scriptcontext_t *context) { - // Reg functions - lua_register(context->luaState, "eventSubscribe", moduleEventSubscribe); -} \ No newline at end of file diff --git a/src/dusk/script/module/event/moduleevent.h b/src/dusk/script/module/event/moduleevent.h index c7c34f0b..e6bee709 100644 --- a/src/dusk/script/module/event/moduleevent.h +++ b/src/dusk/script/module/event/moduleevent.h @@ -1,21 +1,58 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "event/event.h" -/** - * Register event functions to the given script context. - * - * @param context The script context to register event functions to. - */ -void moduleEvent(scriptcontext_t *context); +static int moduleEventSubscribe(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); -/** - * Script binding for subscribing to an event. - */ -int moduleEventSubscribe(lua_State *L); \ No newline at end of file + scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L); + assertNotNull(context, "Script context cannot be NULL"); + + if(!lua_islightuserdata(L, 1)) { + luaL_error(L, "eventSubscribe: Expected event pointer as first argument"); + return 0; + } + + if(!lua_isfunction(L, 2)) { + luaL_error(L, "eventSubscribe: Expected function as second argument"); + return 0; + } + + event_t *event = (event_t *)lua_touserdata(L, 1); + assertNotNull(event, "Event cannot be NULL"); + + eventsub_t id = eventSubscribeScriptContext(event, context, 2); + 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); + eventUnsubscribe(event, id); + return 0; +} + +static void moduleEvent(lua_State *L) { + lua_register(L, "eventSubscribe", moduleEventSubscribe); + lua_register(L, "eventUnsubscribe", moduleEventUnsubscribe); +} diff --git a/src/dusk/script/module/input/CMakeLists.txt b/src/dusk/script/module/input/CMakeLists.txt deleted file mode 100644 index 928bd2de..00000000 --- a/src/dusk/script/module/input/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2025 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleinput.c -) \ No newline at end of file diff --git a/src/dusk/script/module/input/moduleinput.c b/src/dusk/script/module/input/moduleinput.c deleted file mode 100644 index d9293a53..00000000 --- a/src/dusk/script/module/input/moduleinput.c +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleinput.h" -#include "input/input.h" -#include "assert/assert.h" -#include "util/string.h" - -void moduleInput(scriptcontext_t *context) { - assertNotNull(context, "Script context cannot be NULL"); - - // Setup enums. - scriptContextExec(context, INPUT_ACTION_SCRIPT); - - // Input values. - scriptContextExec(context, - "" - #ifdef DUSK_INPUT_KEYBOARD - "INPUT_KEYBOARD = true\n" - #endif - #ifdef DUSK_INPUT_GAMEPAD - "INPUT_GAMEPAD = true\n" - #endif - #ifdef DUSK_INPUT_POINTER - "INPUT_POINTER = true\n" - #endif - #ifdef DUSK_INPUT_TOUCH - "INPUT_TOUCH = true\n" - #endif - ); - - // Metatable - if(luaL_newmetatable(context->luaState, "input_mt")) { - lua_pushcfunction(context->luaState, moduleInputIndex); - lua_setfield(context->luaState, -2, "__index"); - } - lua_pop(context->luaState, 1); - - // Events - lua_pushlightuserdata(context->luaState, &INPUT.eventPressed); - lua_setglobal(context->luaState, "INPUT_EVENT_PRESSED"); - - lua_pushlightuserdata(context->luaState, &INPUT.eventReleased); - lua_setglobal(context->luaState, "INPUT_EVENT_RELEASED"); - - // Bind methods - lua_register(context->luaState, "inputBind", moduleInputBind); - lua_register(context->luaState, "inputIsDown", moduleInputIsDown); - lua_register(context->luaState, "inputPressed", moduleInputPressed); - lua_register(context->luaState, "inputReleased", moduleInputReleased); - lua_register(context->luaState, "inputGetValue", moduleInputGetValue); - lua_register(context->luaState, "inputAxis", moduleInputAxis); -} - -int moduleInputIndex(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL"); - - const char_t *key = luaL_checkstring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - if(stringCompare(key, "action") == 0) { - const inputevent_t *event = (const inputevent_t*)lua_touserdata(l, 1); - if(event == NULL) { - luaL_error(l, "Expected input event as first argument"); - return 0; - } - - lua_pushnumber(l, event->action); - return 1; - } - - lua_pushnil(l); - return 1; -} - -int moduleInputBind(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Requires action and button. - if(!lua_isstring(L, 1)) { - luaL_error(L, "inputBind: Expected button name as first argument"); - return 0; - } - - // Expect action ID - if(!lua_isnumber(L, 2)) { - luaL_error(L, "inputBind: Expected action ID as second argument"); - return 0; - } - - const char_t *strBtn = lua_tostring(L, 1); - const inputaction_t action = (inputaction_t)lua_tonumber(L, 2); - - if(strBtn == NULL || strlen(strBtn) == 0) { - luaL_error(L, "inputBind: Button name cannot be NULL or empty"); - return 0; - } - - // Validate action - if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - luaL_error(L, "inputBind: Invalid action ID %d with str %s", action, strBtn); - return 0; - } - - // Get button by name - inputbutton_t btn = inputButtonGetByName(strBtn); - if(btn.type == INPUT_BUTTON_TYPE_NONE) { - luaL_error(L, "inputBind: Invalid button name '%s'", strBtn); - return 0; - } - - inputBind(btn, action); - return 0; -} - -int moduleInputIsDown(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - if(!lua_isnumber(L, 1)) { - luaL_error(L, "inputIsDown: Expected action ID as first argument"); - return 0; - } - - const inputaction_t action = (inputaction_t)lua_tonumber(L, 1); - - if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - luaL_error(L, "inputIsDown: Invalid action ID %d", action); - return 0; - } - - lua_pushboolean(L, inputIsDown(action)); - return 1; -} - -int moduleInputPressed(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - if(!lua_isnumber(L, 1)) { - luaL_error(L, "inputPressed: Expected action ID as first argument"); - return 0; - } - - const inputaction_t action = (inputaction_t)lua_tonumber(L, 1); - - if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - luaL_error(L, "inputPressed: Invalid action ID %d", action); - return 0; - } - - lua_pushboolean(L, inputPressed(action)); - return 1; -} - -int moduleInputReleased(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - if(!lua_isnumber(L, 1)) { - luaL_error(L, "inputReleased: Expected action ID as first argument"); - return 0; - } - - const inputaction_t action = (inputaction_t)lua_tonumber(L, 1); - - if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - luaL_error(L, "inputReleased: Invalid action ID %d", action); - return 0; - } - - lua_pushboolean(L, inputReleased(action)); - return 1; -} - -int moduleInputGetValue(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - if(!lua_isnumber(L, 1)) { - luaL_error(L, "inputGetValue: Expected action ID as first argument"); - return 0; - } - - const inputaction_t action = (inputaction_t)lua_tonumber(L, 1); - if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - luaL_error(L, "inputGetValue: Invalid action ID %d", action); - return 0; - } - - lua_pushnumber(L, inputGetCurrentValue(action)); - return 1; -} - -int moduleInputAxis(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - if(!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { - luaL_error(L, "inputAxis: Expected two action IDs as arguments (neg, pos)"); - return 0; - } - - const inputaction_t neg = (inputaction_t)lua_tonumber(L, 1); - const inputaction_t pos = (inputaction_t)lua_tonumber(L, 2); - - if(neg < INPUT_ACTION_NULL || neg >= INPUT_ACTION_COUNT) { - luaL_error(L, "inputAxis: Invalid negative action ID %d", neg); - return 0; - } - if(pos < INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) { - luaL_error(L, "inputAxis: Invalid positive action ID %d", pos); - return 0; - } - - lua_pushnumber(L, inputAxis(neg, pos)); - return 1; -} \ No newline at end of file diff --git a/src/dusk/script/module/input/moduleinput.h b/src/dusk/script/module/input/moduleinput.h index ff3e9053..a953cf55 100644 --- a/src/dusk/script/module/input/moduleinput.h +++ b/src/dusk/script/module/input/moduleinput.h @@ -1,71 +1,206 @@ /** * Copyright (c) 2025 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "input/input.h" -/** - * Register input functions to the given script context. - * - * @param context The script context to register input functions to. - */ -void moduleInput(scriptcontext_t *context); +static int moduleInputIndex(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL"); -/** - * Getter for input structure fields. - * - * @param l The Lua state. - */ -int moduleInputIndex(lua_State *l); + const char_t *key = luaL_checkstring(l, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); -/** - * Script binding for binding an input button to an action. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInputBind(lua_State *L); + 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; + } -/** - * Script binding for checking if an input action is currently pressed. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInputIsDown(lua_State *L); + lua_pushnil(l); + return 1; +} -/** - * Script binding for checking if an input action was pressed this frame. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInputPressed(lua_State *L); +static int moduleInputBind(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); -/** - * Script binding for checking if an input action was released this frame. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInputReleased(lua_State *L); + if(!lua_isstring(L, 1)) { + luaL_error(L, "inputBind: Expected button name as first argument"); + return 0; + } -/** - * Script binding for getting the value of an input axis. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInputGetValue(lua_State *L); + if(!lua_isnumber(L, 2)) { + luaL_error(L, "inputBind: Expected action ID as second argument"); + return 0; + } -/** - * Script binding for inputAxis. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInputAxis(lua_State *L); \ No newline at end of file + 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; + } + + if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { + luaL_error(L, "inputBind: Invalid action ID %d with str %s", action, strBtn); + return 0; + } + + inputbutton_t btn = inputButtonGetByName(strBtn); + if(btn.type == INPUT_BUTTON_TYPE_NONE) { + luaL_error(L, "inputBind: Invalid button name '%s'", strBtn); + return 0; + } + + inputBind(btn, action); + return 0; +} + +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); + + if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { + luaL_error(L, "inputIsDown: Invalid action ID %d", action); + return 0; + } + + lua_pushboolean(L, inputIsDown(action)); + return 1; +} + +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); + + if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { + luaL_error(L, "inputPressed: Invalid action ID %d", action); + return 0; + } + + lua_pushboolean(L, inputPressed(action)); + return 1; +} + +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); + + if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { + luaL_error(L, "inputReleased: Invalid action ID %d", action); + return 0; + } + + lua_pushboolean(L, inputReleased(action)); + return 1; +} + +static int moduleInputGetValue(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + if(!lua_isnumber(L, 1)) { + luaL_error(L, "inputGetValue: Expected action ID as first argument"); + return 0; + } + + const inputaction_t action = (inputaction_t)lua_tonumber(L, 1); + if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { + luaL_error(L, "inputGetValue: Invalid action ID %d", action); + return 0; + } + + lua_pushnumber(L, inputGetCurrentValue(action)); + return 1; +} + +static int moduleInputAxis(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + if(!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { + luaL_error(L, "inputAxis: Expected two action IDs as arguments (neg, pos)"); + return 0; + } + + const inputaction_t neg = (inputaction_t)lua_tonumber(L, 1); + const inputaction_t pos = (inputaction_t)lua_tonumber(L, 2); + + if(neg < INPUT_ACTION_NULL || neg >= INPUT_ACTION_COUNT) { + luaL_error(L, "inputAxis: Invalid negative action ID %d", neg); + return 0; + } + if(pos < INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) { + luaL_error(L, "inputAxis: Invalid positive action ID %d", pos); + return 0; + } + + lua_pushnumber(L, inputAxis(neg, pos)); + return 1; +} + +static void moduleInput(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + luaL_dostring(L, INPUT_ACTION_SCRIPT); + + luaL_dostring(L, + "" + #ifdef DUSK_INPUT_KEYBOARD + "INPUT_KEYBOARD = true\n" + #endif + #ifdef DUSK_INPUT_GAMEPAD + "INPUT_GAMEPAD = true\n" + #endif + #ifdef DUSK_INPUT_POINTER + "INPUT_POINTER = true\n" + #endif + #ifdef DUSK_INPUT_TOUCH + "INPUT_TOUCH = true\n" + #endif + ); + + 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); +} diff --git a/src/dusk/script/module/locale/CMakeLists.txt b/src/dusk/script/module/locale/CMakeLists.txt deleted file mode 100644 index fc5b99b5..00000000 --- a/src/dusk/script/module/locale/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - modulelocale.c -) \ No newline at end of file diff --git a/src/dusk/script/module/locale/modulelocale.c b/src/dusk/script/module/locale/modulelocale.c deleted file mode 100644 index ef0312fe..00000000 --- a/src/dusk/script/module/locale/modulelocale.c +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulelocale.h" -#include "locale/localemanager.h" -#include "assert/assert.h" - -void moduleLocale(scriptcontext_t *context) { - assertNotNull(context, "Script context cannot be NULL"); - - // Execute the locale script definitions. - lua_register(context->luaState, "localeGetText", moduleLocaleGetText); -} - -int moduleLocaleGetText(lua_State *L) { - // Expect string param for the id - if(!lua_isstring(L, 1)) { - luaL_error(L, "Expected message ID as first argument"); - return 0; - } - - const char_t *id = lua_tostring(L, 1); - if(id == NULL || strlen(id) == 0) { - luaL_error(L, "Message ID cannot be NULL or empty"); - return 0; - } - - // Optional plural param, default to 0 - int32_t plural = 0; - int top = lua_gettop(L); - int argStart = 2; - - if(top >= 2 && !lua_isnil(L, 2)) { - if(!lua_isnumber(L, 2)) { - luaL_error(L, "Expected plural as second argument"); - return 0; - } - - plural = (int32_t)lua_tointeger(L, 2); - if(plural < 0) { - luaL_error(L, "Plural cannot be negative"); - return 0; - } - - argStart = 3; - } - - // Build structured arg list from remaining Lua args - size_t argCount = (top >= argStart) ? (size_t)(top - argStart + 1) : 0; - #define MODULE_LOCALE_MAX_ARGS 16 - assetlocalearg_t argsStack[MODULE_LOCALE_MAX_ARGS]; - assetlocalearg_t *args = argsStack; - - if(argCount > MODULE_LOCALE_MAX_ARGS) { - luaL_error(L, "Too many args (max %d)", MODULE_LOCALE_MAX_ARGS); - return 0; - } - - for(size_t i = 0; i < argCount; ++i) { - int luaIndex = argStart + (int)i; - - if(lua_isinteger(L, luaIndex)) { - args[i].type = ASSET_LOCALE_ARG_INT; - args[i].intValue = (int32_t)lua_tonumber(L, luaIndex); - } else if(lua_isnumber(L, luaIndex)) { - args[i].type = ASSET_LOCALE_ARG_FLOAT; - args[i].floatValue = lua_tonumber(L, luaIndex); - } else if(lua_isstring(L, luaIndex)) { - args[i].type = ASSET_LOCALE_ARG_STRING; - args[i].stringValue = lua_tostring(L, luaIndex); - } else { - luaL_error(L, "Unsupported localization argument type"); - return 0; - } - } - - char_t buffer[1024]; - errorret_t err = localeManagerGetTextArgs( - id, - buffer, - sizeof(buffer), - plural, - args, - argCount - ); - - if(err.code != ERROR_OK) { - errorCatch(errorPrint(err)); - luaL_error(L, "Failed to get localized text for ID '%s'", id); - return 0; - } - - lua_pushstring(L, buffer); - return 1; -} \ No newline at end of file diff --git a/src/dusk/script/module/locale/modulelocale.h b/src/dusk/script/module/locale/modulelocale.h index b93c6c63..8701b416 100644 --- a/src/dusk/script/module/locale/modulelocale.h +++ b/src/dusk/script/module/locale/modulelocale.h @@ -1,24 +1,85 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" +#include "locale/localemanager.h" -/** - * Register locale functions to the given script context. - * - * @param context The script context to register locale functions to. - */ -void moduleLocale(scriptcontext_t *context); +static int moduleLocaleGetText(lua_State *L) { + if(!lua_isstring(L, 1)) { + luaL_error(L, "Expected message ID as first argument"); + return 0; + } -/** - * Script binding for getting a localized string. - * - * @param L The Lua state. - * @return The number of return values on the Lua stack. - */ -int moduleLocaleGetText(lua_State *L); \ No newline at end of file + 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; + } + + int32_t plural = 0; + int top = lua_gettop(L); + int argStart = 2; + + if(top >= 2 && !lua_isnil(L, 2)) { + if(!lua_isnumber(L, 2)) { + luaL_error(L, "Expected plural as second argument"); + return 0; + } + plural = (int32_t)lua_tonumber(L, 2); + if(plural < 0) { + luaL_error(L, "Plural cannot be negative"); + return 0; + } + argStart = 3; + } + + size_t argCount = (top >= argStart) ? (size_t)(top - argStart + 1) : 0; + #define MODULE_LOCALE_MAX_ARGS 16 + assetlocalearg_t argsStack[MODULE_LOCALE_MAX_ARGS]; + + if(argCount > MODULE_LOCALE_MAX_ARGS) { + luaL_error(L, "Too many args (max %d)", MODULE_LOCALE_MAX_ARGS); + return 0; + } + + for(size_t i = 0; i < argCount; ++i) { + int luaIndex = argStart + (int)i; + if(lua_isinteger(L, luaIndex)) { + 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); + } else { + luaL_error(L, "Unsupported localization argument type"); + return 0; + } + } + #undef MODULE_LOCALE_MAX_ARGS + + char_t buffer[1024]; + errorret_t err = localeManagerGetTextArgs( + id, buffer, sizeof(buffer), plural, argsStack, argCount + ); + if(err.code != ERROR_OK) { + errorCatch(errorPrint(err)); + luaL_error(L, "Failed to get localized text for ID '%s'", id); + return 0; + } + + lua_pushstring(L, buffer); + return 1; +} + +static void moduleLocale(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + lua_register(L, "localeGetText", moduleLocaleGetText); +} diff --git a/src/dusk/script/module/math/modulemat4.h b/src/dusk/script/module/math/modulemat4.h new file mode 100644 index 00000000..07b0e3a5 --- /dev/null +++ b/src/dusk/script/module/math/modulemat4.h @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/module/modulebase.h" +#include "modulevec3.h" +#include "modulevec4.h" + +static void luaMat4Push(lua_State *L, mat4 m) { + mat4 *u = (mat4 *)lua_newuserdata(L, sizeof(mat4)); + glm_mat4_copy(m, *u); + luaL_getmetatable(L, "mat4_mt"); + lua_setmetatable(L, -2); +} + +static void luaMat4Check(lua_State *L, int idx, mat4 out) { + mat4 *m = (mat4 *)luaL_checkudata(L, idx, "mat4_mt"); + glm_mat4_copy(*m, out); +} + +static int mat4Index(lua_State *L) { + lua_getmetatable(L, 1); + lua_getfield(L, -1, luaL_checkstring(L, 2)); + return 1; +} + +static int mat4OpMul(lua_State *L) { + mat4 *a = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + mat4 *b = (mat4 *)luaL_checkudata(L, 2, "mat4_mt"); + mat4 r; glm_mat4_mul(*a, *b, r); + luaMat4Push(L, r); return 1; +} + +static int mat4OpToString(lua_State *L) { + lua_pushstring(L, "mat4(...)"); return 1; +} + +static int mat4Transpose(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + mat4 r; glm_mat4_transpose_to(*m, r); + luaMat4Push(L, r); return 1; +} + +static int mat4Inverse(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + mat4 r; glm_mat4_inv(*m, r); + luaMat4Push(L, r); return 1; +} + +static int mat4MulVec3(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + vec3 v; luaVec3Check(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); + luaVec3Push(L, r); return 1; +} + +static int mat4MulVec4(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + vec4 v; luaVec4Check(L, 2, v); + vec4 r; glm_mat4_mulv(*m, v, r); + luaVec4Push(L, r); return 1; +} + +static int mat4Translate(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + vec3 v; luaVec3Check(L, 2, v); + mat4 r; glm_mat4_copy(*m, r); + glm_translate(r, v); + luaMat4Push(L, r); return 1; +} + +static int mat4Scale(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + vec3 v; luaVec3Check(L, 2, v); + mat4 r; glm_mat4_copy(*m, r); + glm_scale(r, v); + luaMat4Push(L, r); return 1; +} + +static int mat4Identity(lua_State *L) { + mat4 r; glm_mat4_identity(r); + luaMat4Push(L, r); return 1; +} + +static int mat4Determinant(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + lua_pushnumber(L, (lua_Number)glm_mat4_det(*m)); return 1; +} + +static int mat4Create(lua_State *L) { + mat4 m; glm_mat4_identity(m); + luaMat4Push(L, m); return 1; +} + +static void moduleMathMat4(lua_State *L) { + if(!luaL_newmetatable(L, "mat4_mt")) { lua_pop(L, 1); return; } + + lua_pushcfunction(L, mat4Index); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, mat4OpMul); lua_setfield(L, -2, "__mul"); + lua_pushcfunction(L, mat4OpToString); lua_setfield(L, -2, "__tostring"); + lua_pushcfunction(L, mat4Transpose); lua_setfield(L, -2, "transpose"); + lua_pushcfunction(L, mat4Inverse); lua_setfield(L, -2, "inverse"); + lua_pushcfunction(L, mat4MulVec3); lua_setfield(L, -2, "mulVec3"); + lua_pushcfunction(L, mat4MulVec4); lua_setfield(L, -2, "mulVec4"); + lua_pushcfunction(L, mat4Translate); lua_setfield(L, -2, "translate"); + lua_pushcfunction(L, mat4Scale); lua_setfield(L, -2, "scale"); + lua_pushcfunction(L, mat4Identity); lua_setfield(L, -2, "identity"); + lua_pushcfunction(L, mat4Determinant); lua_setfield(L, -2, "determinant"); + lua_pop(L, 1); + + lua_register(L, "mat4", mat4Create); +} diff --git a/src/dusk/script/module/math/modulemath.h b/src/dusk/script/module/math/modulemath.h new file mode 100644 index 00000000..f55b608c --- /dev/null +++ b/src/dusk/script/module/math/modulemath.h @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/module/modulebase.h" +#include "modulevec2.h" +#include "modulevec3.h" +#include "modulevec4.h" +#include "modulemat4.h" + +static void moduleMath(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + moduleMathVec2(L); + moduleMathVec3(L); + moduleMathVec4(L); + moduleMathMat4(L); +} diff --git a/src/dusk/script/module/math/modulevec2.h b/src/dusk/script/module/math/modulevec2.h new file mode 100644 index 00000000..4f7843c7 --- /dev/null +++ b/src/dusk/script/module/math/modulevec2.h @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/module/modulebase.h" + +static void luaVec2Push(lua_State *L, vec2 v) { + vec2 *u = (vec2 *)lua_newuserdata(L, sizeof(vec2)); + glm_vec2_copy(v, *u); + luaL_getmetatable(L, "vec2_mt"); + lua_setmetatable(L, -2); +} + +static void luaVec2Check(lua_State *L, int idx, vec2 out) { + vec2 *v = (vec2 *)luaL_checkudata(L, idx, "vec2_mt"); + glm_vec2_copy(*v, out); +} + +static int vec2Index(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + const char *key = luaL_checkstring(L, 2); + 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); + return 1; +} + +static int vec2Newindex(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + const char *key = luaL_checkstring(L, 2); + 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; +} + +static int vec2OpAdd(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + vec2 r; glm_vec2_add(*a, *b, r); + luaVec2Push(L, r); return 1; +} + +static int vec2OpSub(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + vec2 r; glm_vec2_sub(*a, *b, r); + luaVec2Push(L, r); return 1; +} + +static int vec2OpMul(lua_State *L) { + 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"); + glm_vec2_scale(*v, s, r); + } else { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + glm_vec2_scale(*v, s, r); + } + luaVec2Push(L, r); return 1; +} + +static int vec2OpDiv(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + vec2 r; glm_vec2_divs(*v, s, r); + luaVec2Push(L, r); return 1; +} + +static int vec2OpUnm(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 r; glm_vec2_negate_to(*v, r); + luaVec2Push(L, r); return 1; +} + +static int vec2OpEq(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1]); + return 1; +} + +static int vec2OpToString(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + char buf[64]; + snprintf(buf, sizeof(buf), "vec2(%.3f, %.3f)", (*v)[0], (*v)[1]); + lua_pushstring(L, buf); return 1; +} + +static int vec2Dot(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + lua_pushnumber(L, (lua_Number)glm_vec2_dot(*a, *b)); return 1; +} + +static int vec2Length(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + lua_pushnumber(L, (lua_Number)glm_vec2_norm(*v)); return 1; +} + +static int vec2LengthSq(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + lua_pushnumber(L, (lua_Number)glm_vec2_norm2(*v)); return 1; +} + +static int vec2Normalize(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 r; glm_vec2_normalize_to(*v, r); + luaVec2Push(L, r); return 1; +} + +static int vec2Lerp(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + float_t t = (float_t)luaL_checknumber(L, 3); + vec2 r; glm_vec2_lerp(*a, *b, t, r); + luaVec2Push(L, r); return 1; +} + +static int vec2Distance(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + lua_pushnumber(L, (lua_Number)glm_vec2_distance(*a, *b)); return 1; +} + +static int vec2Negate(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 r; glm_vec2_negate_to(*v, r); + luaVec2Push(L, r); return 1; +} + +static int vec2Create(lua_State *L) { + 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); + luaVec2Push(L, v); return 1; +} + +static void moduleMathVec2(lua_State *L) { + if(!luaL_newmetatable(L, "vec2_mt")) { lua_pop(L, 1); return; } + + lua_pushcfunction(L, vec2Index); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, vec2Newindex); lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, vec2OpAdd); lua_setfield(L, -2, "__add"); + lua_pushcfunction(L, vec2OpSub); lua_setfield(L, -2, "__sub"); + lua_pushcfunction(L, vec2OpMul); lua_setfield(L, -2, "__mul"); + lua_pushcfunction(L, vec2OpDiv); lua_setfield(L, -2, "__div"); + lua_pushcfunction(L, vec2OpUnm); lua_setfield(L, -2, "__unm"); + lua_pushcfunction(L, vec2OpEq); lua_setfield(L, -2, "__eq"); + lua_pushcfunction(L, vec2OpToString); lua_setfield(L, -2, "__tostring"); + lua_pushcfunction(L, vec2Dot); lua_setfield(L, -2, "dot"); + lua_pushcfunction(L, vec2Length); lua_setfield(L, -2, "length"); + lua_pushcfunction(L, vec2LengthSq); lua_setfield(L, -2, "lengthSq"); + lua_pushcfunction(L, vec2Normalize); lua_setfield(L, -2, "normalize"); + lua_pushcfunction(L, vec2Lerp); lua_setfield(L, -2, "lerp"); + lua_pushcfunction(L, vec2Distance); lua_setfield(L, -2, "distance"); + lua_pushcfunction(L, vec2Negate); lua_setfield(L, -2, "negate"); + lua_pop(L, 1); + + lua_register(L, "vec2", vec2Create); +} diff --git a/src/dusk/script/module/math/modulevec3.h b/src/dusk/script/module/math/modulevec3.h new file mode 100644 index 00000000..97146be7 --- /dev/null +++ b/src/dusk/script/module/math/modulevec3.h @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/module/modulebase.h" + +static void luaVec3Push(lua_State *L, vec3 v) { + vec3 *u = (vec3 *)lua_newuserdata(L, sizeof(vec3)); + glm_vec3_copy(v, *u); + luaL_getmetatable(L, "vec3_mt"); + lua_setmetatable(L, -2); +} + +static void luaVec3Check(lua_State *L, int idx, vec3 out) { + vec3 *v = (vec3 *)luaL_checkudata(L, idx, "vec3_mt"); + glm_vec3_copy(*v, out); +} + +static int vec3Index(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + const char *key = luaL_checkstring(L, 2); + 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); + return 1; +} + +static int vec3Newindex(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + const char *key = luaL_checkstring(L, 2); + 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; +} + +static int vec3OpAdd(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + vec3 r; glm_vec3_add(*a, *b, r); + luaVec3Push(L, r); return 1; +} + +static int vec3OpSub(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + vec3 r; glm_vec3_sub(*a, *b, r); + luaVec3Push(L, r); return 1; +} + +static int vec3OpMul(lua_State *L) { + 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"); + glm_vec3_scale(*v, s, r); + } else { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + glm_vec3_scale(*v, s, r); + } + luaVec3Push(L, r); return 1; +} + +static int vec3OpDiv(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + vec3 r; glm_vec3_divs(*v, s, r); + luaVec3Push(L, r); return 1; +} + +static int vec3OpUnm(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 r; glm_vec3_negate_to(*v, r); + luaVec3Push(L, r); return 1; +} + +static int vec3OpEq(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] && (*a)[2] == (*b)[2]); + return 1; +} + +static int vec3OpToString(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + char buf[80]; + snprintf(buf, sizeof(buf), "vec3(%.3f, %.3f, %.3f)", (*v)[0], (*v)[1], (*v)[2]); + lua_pushstring(L, buf); return 1; +} + +static int vec3Dot(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + lua_pushnumber(L, (lua_Number)glm_vec3_dot(*a, *b)); return 1; +} + +static int vec3Cross(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + vec3 r; glm_vec3_cross(*a, *b, r); + luaVec3Push(L, r); return 1; +} + +static int vec3Length(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + lua_pushnumber(L, (lua_Number)glm_vec3_norm(*v)); return 1; +} + +static int vec3LengthSq(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + lua_pushnumber(L, (lua_Number)glm_vec3_norm2(*v)); return 1; +} + +static int vec3Normalize(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 r; glm_vec3_normalize_to(*v, r); + luaVec3Push(L, r); return 1; +} + +static int vec3Lerp(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + float_t t = (float_t)luaL_checknumber(L, 3); + vec3 r; glm_vec3_lerp(*a, *b, t, r); + luaVec3Push(L, r); return 1; +} + +static int vec3Distance(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + lua_pushnumber(L, (lua_Number)glm_vec3_distance(*a, *b)); return 1; +} + +static int vec3Negate(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 r; glm_vec3_negate_to(*v, r); + luaVec3Push(L, r); return 1; +} + +static int vec3Create(lua_State *L) { + 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); + luaVec3Push(L, v); return 1; +} + +static void moduleMathVec3(lua_State *L) { + if(!luaL_newmetatable(L, "vec3_mt")) { lua_pop(L, 1); return; } + + lua_pushcfunction(L, vec3Index); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, vec3Newindex); lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, vec3OpAdd); lua_setfield(L, -2, "__add"); + lua_pushcfunction(L, vec3OpSub); lua_setfield(L, -2, "__sub"); + lua_pushcfunction(L, vec3OpMul); lua_setfield(L, -2, "__mul"); + lua_pushcfunction(L, vec3OpDiv); lua_setfield(L, -2, "__div"); + lua_pushcfunction(L, vec3OpUnm); lua_setfield(L, -2, "__unm"); + lua_pushcfunction(L, vec3OpEq); lua_setfield(L, -2, "__eq"); + lua_pushcfunction(L, vec3OpToString); lua_setfield(L, -2, "__tostring"); + lua_pushcfunction(L, vec3Dot); lua_setfield(L, -2, "dot"); + lua_pushcfunction(L, vec3Cross); lua_setfield(L, -2, "cross"); + lua_pushcfunction(L, vec3Length); lua_setfield(L, -2, "length"); + lua_pushcfunction(L, vec3LengthSq); lua_setfield(L, -2, "lengthSq"); + lua_pushcfunction(L, vec3Normalize); lua_setfield(L, -2, "normalize"); + lua_pushcfunction(L, vec3Lerp); lua_setfield(L, -2, "lerp"); + lua_pushcfunction(L, vec3Distance); lua_setfield(L, -2, "distance"); + lua_pushcfunction(L, vec3Negate); lua_setfield(L, -2, "negate"); + lua_pop(L, 1); + + lua_register(L, "vec3", vec3Create); +} diff --git a/src/dusk/script/module/math/modulevec4.h b/src/dusk/script/module/math/modulevec4.h new file mode 100644 index 00000000..cad467c2 --- /dev/null +++ b/src/dusk/script/module/math/modulevec4.h @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/module/modulebase.h" + +static void luaVec4Push(lua_State *L, vec4 v) { + vec4 *u = (vec4 *)lua_newuserdata(L, sizeof(vec4)); + glm_vec4_copy(v, *u); + luaL_getmetatable(L, "vec4_mt"); + lua_setmetatable(L, -2); +} + +static void luaVec4Check(lua_State *L, int idx, vec4 out) { + vec4 *v = (vec4 *)luaL_checkudata(L, idx, "vec4_mt"); + glm_vec4_copy(*v, out); +} + +static int vec4Index(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + const char *key = luaL_checkstring(L, 2); + if(stringCompare(key, "x") == 0 || stringCompare(key, "u0") == 0) { lua_pushnumber(L, (lua_Number)(*v)[0]); return 1; } + if(stringCompare(key, "y") == 0 || stringCompare(key, "v0") == 0) { lua_pushnumber(L, (lua_Number)(*v)[1]); return 1; } + if(stringCompare(key, "z") == 0 || stringCompare(key, "u1") == 0) { lua_pushnumber(L, (lua_Number)(*v)[2]); return 1; } + if(stringCompare(key, "w") == 0 || stringCompare(key, "v1") == 0) { lua_pushnumber(L, (lua_Number)(*v)[3]); return 1; } + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + return 1; +} + +static int vec4Newindex(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + const char *key = luaL_checkstring(L, 2); + if(stringCompare(key, "x") == 0 || stringCompare(key, "u0") == 0) { (*v)[0] = (float_t)luaL_checknumber(L, 3); return 0; } + if(stringCompare(key, "y") == 0 || stringCompare(key, "v0") == 0) { (*v)[1] = (float_t)luaL_checknumber(L, 3); return 0; } + if(stringCompare(key, "z") == 0 || stringCompare(key, "u1") == 0) { (*v)[2] = (float_t)luaL_checknumber(L, 3); return 0; } + if(stringCompare(key, "w") == 0 || stringCompare(key, "v1") == 0) { (*v)[3] = (float_t)luaL_checknumber(L, 3); return 0; } + luaL_error(L, "vec4: unknown property '%s'", key); + return 0; +} + +static int vec4OpAdd(lua_State *L) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + vec4 r; glm_vec4_add(*a, *b, r); + luaVec4Push(L, r); return 1; +} + +static int vec4OpSub(lua_State *L) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + vec4 r; glm_vec4_sub(*a, *b, r); + luaVec4Push(L, r); return 1; +} + +static int vec4OpMul(lua_State *L) { + 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"); + glm_vec4_scale(*v, s, r); + } else { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + glm_vec4_scale(*v, s, r); + } + luaVec4Push(L, r); return 1; +} + +static int vec4OpDiv(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + vec4 r; glm_vec4_divs(*v, s, r); + luaVec4Push(L, r); return 1; +} + +static int vec4OpUnm(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 r; glm_vec4_negate_to(*v, r); + luaVec4Push(L, r); return 1; +} + +static int vec4OpEq(lua_State *L) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] && (*a)[2] == (*b)[2] && (*a)[3] == (*b)[3]); + return 1; +} + +static int vec4OpToString(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + 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 int vec4Dot(lua_State *L) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + lua_pushnumber(L, (lua_Number)glm_vec4_dot(*a, *b)); return 1; +} + +static int vec4Length(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + lua_pushnumber(L, (lua_Number)glm_vec4_norm(*v)); return 1; +} + +static int vec4LengthSq(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + lua_pushnumber(L, (lua_Number)glm_vec4_norm2(*v)); return 1; +} + +static int vec4Normalize(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 r; glm_vec4_normalize_to(*v, r); + luaVec4Push(L, r); return 1; +} + +static int vec4Lerp(lua_State *L) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + float_t t = (float_t)luaL_checknumber(L, 3); + vec4 r; glm_vec4_lerp(*a, *b, t, r); + luaVec4Push(L, r); return 1; +} + +static int vec4Negate(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 r; glm_vec4_negate_to(*v, r); + luaVec4Push(L, r); return 1; +} + +static int vec4Create(lua_State *L) { + 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); + luaVec4Push(L, v); return 1; +} + +static void moduleMathVec4(lua_State *L) { + if(!luaL_newmetatable(L, "vec4_mt")) { lua_pop(L, 1); return; } + + lua_pushcfunction(L, vec4Index); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, vec4Newindex); lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, vec4OpAdd); lua_setfield(L, -2, "__add"); + lua_pushcfunction(L, vec4OpSub); lua_setfield(L, -2, "__sub"); + lua_pushcfunction(L, vec4OpMul); lua_setfield(L, -2, "__mul"); + lua_pushcfunction(L, vec4OpDiv); lua_setfield(L, -2, "__div"); + lua_pushcfunction(L, vec4OpUnm); lua_setfield(L, -2, "__unm"); + lua_pushcfunction(L, vec4OpEq); lua_setfield(L, -2, "__eq"); + lua_pushcfunction(L, vec4OpToString); lua_setfield(L, -2, "__tostring"); + lua_pushcfunction(L, vec4Dot); lua_setfield(L, -2, "dot"); + lua_pushcfunction(L, vec4Length); lua_setfield(L, -2, "length"); + lua_pushcfunction(L, vec4LengthSq); lua_setfield(L, -2, "lengthSq"); + lua_pushcfunction(L, vec4Normalize); lua_setfield(L, -2, "normalize"); + lua_pushcfunction(L, vec4Lerp); lua_setfield(L, -2, "lerp"); + lua_pushcfunction(L, vec4Negate); lua_setfield(L, -2, "negate"); + lua_pop(L, 1); + + lua_register(L, "vec4", vec4Create); +} diff --git a/src/dusk/script/module/module.h b/src/dusk/script/module/module.h new file mode 100644 index 00000000..c059eb96 --- /dev/null +++ b/src/dusk/script/module/module.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/module/script/modulescript.h" +#include "script/module/entity/moduleentity.h" +#include "script/module/input/moduleinput.h" +#include "script/module/moduleplatform.h" +#include "script/module/locale/modulelocale.h" +#include "script/module/time/moduletime.h" +#include "script/module/event/moduleevent.h" +#include "script/module/display/modulecolor.h" +#include "script/module/display/modulespritebatch.h" +#include "script/module/math/modulemath.h" +#include "script/module/display/moduleshader.h" +#include "script/module/ui/moduleui.h" +#include "script/module/display/moduletext.h" +#include "script/module/display/modulescreen.h" +#include "script/module/display/moduletexture.h" +#include "script/module/display/moduletileset.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); +} \ No newline at end of file diff --git a/src/dusk/script/module/entity/display/moduleentitymesh.h b/src/dusk/script/module/modulebase.h similarity index 67% rename from src/dusk/script/module/entity/display/moduleentitymesh.h rename to src/dusk/script/module/modulebase.h index d333c90a..d7183a64 100644 --- a/src/dusk/script/module/entity/display/moduleentitymesh.h +++ b/src/dusk/script/module/modulebase.h @@ -1,13 +1,12 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once #include "script/scriptcontext.h" - -void moduleEntityMesh(scriptcontext_t *ctx); - -int moduleEntityMeshAdd(lua_State *L); +#include "assert/assert.h" +#include "util/string.h" +#include "util/memory.h" \ No newline at end of file diff --git a/src/dusk/script/module/moduleplatform.h b/src/dusk/script/module/moduleplatform.h index a45f5695..2920c20c 100644 --- a/src/dusk/script/module/moduleplatform.h +++ b/src/dusk/script/module/moduleplatform.h @@ -16,12 +16,12 @@ #define MODULE_PLATFORM_VALUE "PLATFORM = '" DUSK_TARGET_SYSTEM "'\n" -void modulePlatform(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); +static void modulePlatform(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); - scriptContextExec(ctx, MODULE_PLATFORM_VALUE); + luaL_dostring(L, MODULE_PLATFORM_VALUE); #ifdef modulePlatformPlatform - modulePlatformPlatform(ctx); + modulePlatformPlatform(L); #endif } \ No newline at end of file diff --git a/src/dusk/script/module/scene/CMakeLists.txt b/src/dusk/script/module/scene/CMakeLists.txt deleted file mode 100644 index 9b099835..00000000 --- a/src/dusk/script/module/scene/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - modulescene.c -) \ No newline at end of file diff --git a/src/dusk/script/module/scene/modulescene.c b/src/dusk/script/module/scene/modulescene.c deleted file mode 100644 index b0024a5c..00000000 --- a/src/dusk/script/module/scene/modulescene.c +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulescene.h" -#include "assert/assert.h" -#include "scene/scene.h" - -void moduleScene(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - lua_register(ctx->luaState, "sceneSet", moduleSceneSet); -} - -int moduleSceneSet(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Need string - if(!lua_isstring(L, 1)) { - luaL_error(L, "sceneSet requires a string argument"); - return 0; - } - - const char_t *script = lua_tostring(L, 1); - - sceneSet(script); - - return 0; -} \ No newline at end of file diff --git a/src/dusk/script/module/scene/modulescene.h b/src/dusk/script/module/scene/modulescene.h deleted file mode 100644 index 773d05f4..00000000 --- a/src/dusk/script/module/scene/modulescene.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -/** - * Register the scene module within the given script context. - * - * @param ctx The script context to register the module in. - */ -void moduleScene(scriptcontext_t *ctx); - -/** - * Lua binding for sceneSet function. - * - * @param L The Lua state. - * @return int Number of return values on the Lua stack. - */ -int moduleSceneSet(lua_State *L); \ No newline at end of file diff --git a/src/dusk/script/module/script/modulescript.h b/src/dusk/script/module/script/modulescript.h new file mode 100644 index 00000000..2207ae23 --- /dev/null +++ b/src/dusk/script/module/script/modulescript.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/module/modulebase.h" +#include "console/console.h" + +static int moduleScriptPrint(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + int n = lua_gettop(L); + luaL_Buffer b; + luaL_buffinit(L, &b); + + 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); + } + + luaL_pushresult(&b); + const char *msg = lua_tostring(L, -1); + consolePrint("%s", msg); + return 0; +} + +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; + } + + scriptcontext_t* ctx = *(scriptcontext_t**)lua_getextraspace(L); + if(ctx == NULL) { + luaL_error(L, "Script context is NULL"); + return 0; + } + + const char_t *filename = luaL_checkstring(L, 1); + if(filename == NULL || filename[0] == '\0') { + luaL_error(L, "Filename cannot be NULL"); + return 0; + } + + char_t buffer[1024]; + stringCopy(buffer, filename, 1024); + + 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; + } + stringCopy(&buffer[len], ".lua", 5); + } + + int32_t stackBase = lua_gettop(L); + + errorret_t err = scriptContextExecFile(ctx, buffer); + if(err.code != ERROR_OK) { + luaL_error(L, "Failed to include script file: %s", buffer); + errorCatch(errorPrint(err)); + return 0; + } + + return lua_gettop(L) - stackBase; +} + +static void moduleScript(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + lua_register(L, "print", moduleScriptPrint); + lua_register(L, "include", moduleScriptInclude); +} diff --git a/src/dusk/script/module/system/CMakeLists.txt b/src/dusk/script/module/system/CMakeLists.txt deleted file mode 100644 index 8ec9c9c5..00000000 --- a/src/dusk/script/module/system/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - modulescript.c -) \ No newline at end of file diff --git a/src/dusk/script/module/system/modulescript.c b/src/dusk/script/module/system/modulescript.c deleted file mode 100644 index 5ab48760..00000000 --- a/src/dusk/script/module/system/modulescript.c +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulescript.h" -#include "assert/assert.h" -#include "console/console.h" -#include "util/string.h" -#include "script/scriptmodule.h" - -void moduleScript(scriptcontext_t *context) { - assertNotNull(context, "Script context cannot be NULL"); - - lua_register(context->luaState, "print", moduleScriptPrint); - lua_register(context->luaState, "include", moduleScriptInclude); - lua_register(context->luaState, "module", moduleScriptModule); -} - -int moduleScriptPrint(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - int n = lua_gettop(L); - luaL_Buffer b; - luaL_buffinit(L, &b); - - 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); - } - - luaL_pushresult(&b); - const char *msg = lua_tostring(L, -1); - consolePrint("%s", msg); - return 0; -} - -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; - } - - scriptcontext_t* ctx = *(scriptcontext_t**)lua_getextraspace(L); - if(ctx == NULL) { - luaL_error(L, "Script context is NULL"); - return 0; - } - - const char_t *filename = luaL_checkstring(L, 1); - if(filename == NULL || filename[0] == '\0') { - luaL_error(L, "Filename cannot be NULL"); - return 0; - } - - // Copy out filename to mutable buffer - char_t buffer[1024]; - stringCopy(buffer, filename, 1024); - - // Ensure it has .lua extension - size_t len = strlen(buffer); - if(len < 4 || strcmp(&buffer[len - 4], ".lua") != 0) { - // Append .lua - if(len + 4 >= 1024) { - luaL_error(L, "Filename too long to append .lua"); - return 0; - } - stringCopy(&buffer[len], ".lua", 5); - } - - // Execute the script file - errorret_t err = scriptContextExecFile( - ctx, - buffer - ); - if(err.code != ERROR_OK) { - luaL_error(L, "Failed to include script file"); - errorCatch(errorPrint(err)); - return 0; - } - - return 0; -} - -int moduleScriptModule(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - if(!lua_isstring(L, 1)) { - luaL_error(L, "Expected string module name"); - return 0; - } - - const char_t *moduleName = luaL_checkstring(L, 1); - if(moduleName == NULL) { - luaL_error(L, "Module name cannot be NULL"); - return 0; - } - - const scriptmodule_t *module = scriptModuleGetByName(moduleName); - if(module == NULL) { - luaL_error(L, "Module '%s' not found", moduleName); - return 0; - } - - scriptcontext_t* ctx = *(scriptcontext_t**)lua_getextraspace(L); - if(ctx == NULL) { - luaL_error(L, "Script context is NULL"); - return 0; - } - - module->callback(ctx); - - return 0; -} diff --git a/src/dusk/script/module/system/modulescript.h b/src/dusk/script/module/system/modulescript.h deleted file mode 100644 index a089d10f..00000000 --- a/src/dusk/script/module/system/modulescript.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -/** - * Register system module functions to the given script context. - * - * @param context The script context to register system module functions to. - */ -void moduleScript(scriptcontext_t *context); - -/** - * Script binding for printing messages to the debug console. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleScriptPrint(lua_State *L); - -/** - * Script binding for including and executing another script file. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleScriptInclude(lua_State *L); - -/** - * Script binding for loading a script module by name. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleScriptModule(lua_State *L); diff --git a/src/dusk/script/module/time/CMakeLists.txt b/src/dusk/script/module/time/CMakeLists.txt deleted file mode 100644 index 8fd83bcb..00000000 --- a/src/dusk/script/module/time/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduletime.c -) \ No newline at end of file diff --git a/src/dusk/script/module/time/moduletime.c b/src/dusk/script/module/time/moduletime.c deleted file mode 100644 index 60eda525..00000000 --- a/src/dusk/script/module/time/moduletime.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletime.h" -#include "assert/assert.h" - -void moduleTime(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - - // Script structure - if(luaL_newmetatable(ctx->luaState, "time_mt")) { - lua_pushcfunction(ctx->luaState, moduleTimeIndex); - lua_setfield(ctx->luaState, -2, "__index"); - } - lua_pop(ctx->luaState, 1); - - dusktime_t **ud = (dusktime_t**)lua_newuserdata(ctx->luaState, sizeof(dusktime_t*)); - *ud = &TIME; - luaL_setmetatable(ctx->luaState, "time_mt"); - lua_setglobal(ctx->luaState, "TIME"); -} - -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; - } - - lua_pushnil(L); - return 1; -} \ No newline at end of file diff --git a/src/dusk/script/module/time/moduletime.h b/src/dusk/script/module/time/moduletime.h index d6b3070b..69b07190 100644 --- a/src/dusk/script/module/time/moduletime.h +++ b/src/dusk/script/module/time/moduletime.h @@ -1,25 +1,41 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" +#include "script/module/modulebase.h" #include "time/time.h" -#include "util/string.h" -/** - * Registers the time module within the given script context. - * - * @param ctx The script context to register the module in. - */ -void moduleTime(scriptcontext_t *ctx); +static int moduleTimeIndex(lua_State *L) { + const char_t *key = lua_tostring(L, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); -/** - * Index function for the time structure. - * @param L The Lua state. - * @return Number of return values. - */ -int moduleTimeIndex(lua_State *L); \ No newline at end of file + 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; + } + + lua_pushnil(L); + return 1; +} + +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"); +} diff --git a/src/dusk/script/module/ui/CMakeLists.txt b/src/dusk/script/module/ui/CMakeLists.txt deleted file mode 100644 index 8fc42de6..00000000 --- a/src/dusk/script/module/ui/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleui.c -) \ No newline at end of file diff --git a/src/dusk/script/module/ui/moduleui.c b/src/dusk/script/module/ui/moduleui.c deleted file mode 100644 index f71ad19e..00000000 --- a/src/dusk/script/module/ui/moduleui.c +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleui.h" -#include "assert/assert.h" -#include "ui/ui.h" - -void moduleUi(scriptcontext_t *ctx) { - assertNotNull(ctx, "Script context cannot be NULL"); - -} \ No newline at end of file diff --git a/src/dusk/script/module/ui/moduleui.h b/src/dusk/script/module/ui/moduleui.h index 3e65ea4c..78dc99e8 100644 --- a/src/dusk/script/module/ui/moduleui.h +++ b/src/dusk/script/module/ui/moduleui.h @@ -1,17 +1,13 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once -#include "script/scriptcontext.h" -#include "error/error.h" +#include "script/module/modulebase.h" -/** - * Registers the ui module within the given script context. - * - * @param ctx The script context to register the module in. - */ -void moduleUi(scriptcontext_t *ctx); \ No newline at end of file +static void moduleUi(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); +} diff --git a/src/dusk/script/scriptcontext.c b/src/dusk/script/scriptcontext.c index e8ae1ad5..8e971e40 100644 --- a/src/dusk/script/scriptcontext.c +++ b/src/dusk/script/scriptcontext.c @@ -9,9 +9,13 @@ #include "assert/assert.h" #include "asset/asset.h" #include "util/memory.h" -#include "script/scriptmodule.h" #include "event/event.h" #include "asset/loader/script/assetscriptloader.h" +#include "script/module/module.h" + +#ifdef SCRIPT_GAME_INIT + #include "script/scriptgame.h" +#endif errorret_t scriptContextInit(scriptcontext_t *context) { assertNotNull(context, "Script context cannot be NULL"); @@ -27,13 +31,14 @@ errorret_t scriptContextInit(scriptcontext_t *context) { // Store context in Lua extraspace *(scriptcontext_t**)lua_getextraspace(context->luaState) = context; - - // All scripts get the script module - const scriptmodule_t *sysScript = scriptModuleGetByName("script"); - if(sysScript == NULL) { - errorThrow("Failed to find system script module"); - } - sysScript->callback(context); + + // Register built-in script modules. + moduleRegister(context->luaState); + + // Fire any game script init function if defined. + #ifdef SCRIPT_GAME_INIT + SCRIPT_GAME_INIT(L); + #endif errorOk(); } diff --git a/src/dusk/script/scriptmanager.c b/src/dusk/script/scriptmanager.c index 684e8e84..9606f411 100644 --- a/src/dusk/script/scriptmanager.c +++ b/src/dusk/script/scriptmanager.c @@ -14,9 +14,13 @@ scriptmanager_t SCRIPT_MANAGER; errorret_t scriptManagerInit() { memoryZero(&SCRIPT_MANAGER, sizeof(scriptmanager_t)); + + errorChain(scriptContextInit(&SCRIPT_MANAGER.mainContext)); + errorOk(); } errorret_t scriptManagerDispose() { + scriptContextDispose(&SCRIPT_MANAGER.mainContext); errorOk(); } \ No newline at end of file diff --git a/src/dusk/script/scriptmodule.c b/src/dusk/script/scriptmodule.c deleted file mode 100644 index d0e7abe2..00000000 --- a/src/dusk/script/scriptmodule.c +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "scriptmodule.h" -#include "script/module/system/modulescript.h" -#include "script/module/entity/moduleentity.h" -#include "script/module/input/moduleinput.h" -#include "script/module/moduleplatform.h" -#include "script/module/scene/modulescene.h" -#include "script/module/locale/modulelocale.h" -#include "script/module/time/moduletime.h" -#include "script/module/event/moduleevent.h" -#include "script/module/display/modulecolor.h" -#include "script/module/display/modulespritebatch.h" -#include "script/module/display/moduleglm.h" -#include "script/module/display/moduleshader.h" -#include "script/module/ui/moduleui.h" -#include "script/module/display/moduletext.h" -#include "script/module/display/modulescreen.h" -#include "script/module/display/moduletexture.h" -#include "script/module/display/moduletileset.h" -#include "script/module/entity/display/moduleentityposition.h" -#include "script/module/entity/display/moduleentitycamera.h" -#include "script/module/entity/display/moduleentitymesh.h" -#include "script/module/entity/display/moduleentitymaterial.h" -#include "script/module/entity/physics/moduleentityphysics.h" -#include "script/module/entity/script/moduleentityscript.h" - -#include "script/scriptgame.h" -#include "util/string.h" - -const scriptmodule_t SCRIPT_MODULE_LIST[] = { - #define REG(strName, fnCallback) { .name = strName, .callback = fnCallback }, - REG("script", moduleScript) - REG("entityposition", moduleEntityPosition) - REG("entitycamera", moduleEntityCamera) - REG("entitymesh", moduleEntityMesh) - REG("entitymaterial", moduleEntityMaterial) - REG("entityphysics", moduleEntityPhysics) - REG("entityscript", moduleEntityScript) - REG("entity", moduleEntity) - REG("input", moduleInput) - REG("platform", modulePlatform) - REG("color", moduleColor) - REG("scene", moduleScene) - REG("locale", moduleLocale) - REG("time", moduleTime) - REG("event", moduleEvent) - REG("spritebatch", moduleSpriteBatch) - REG("glm", moduleGLM) - REG("ui", moduleUi) - REG("text", moduleText) - REG("screen", moduleScreen) - REG("texture", moduleTexture) - REG("tileset", moduleTileset) - REG("shader", moduleShader) - #undef REG - - #ifdef SCRIPT_GAME_LIST - SCRIPT_GAME_LIST - #endif -}; - -#define SCRIPT_MODULE_COUNT ( \ - sizeof(SCRIPT_MODULE_LIST) / sizeof(scriptmodule_t) \ -) - -const scriptmodule_t * scriptModuleGetByName(const char_t *name) { - for(uint_fast8_t i = 0; i < SCRIPT_MODULE_COUNT; i++) { - if(stringCompare(SCRIPT_MODULE_LIST[i].name, name) != 0) continue; - return &SCRIPT_MODULE_LIST[i]; - } - - return NULL; -} \ No newline at end of file diff --git a/src/dusk/script/scriptmodule.h b/src/dusk/script/scriptmodule.h deleted file mode 100644 index da9cf8e8..00000000 --- a/src/dusk/script/scriptmodule.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "scriptcontext.h" - -typedef struct scriptmodule_s { - const char_t *name; - void (*callback)(scriptcontext_t *ctx); -} scriptmodule_t; - -extern const scriptmodule_t SCRIPT_MODULE_LIST[]; - -/** - * Retrieves a script module by its name. - * - * @param name The name of the script module. - * @return Pointer to the script module, or NULL if not found. - */ -const scriptmodule_t * scriptModuleGetByName(const char_t *name); \ No newline at end of file diff --git a/src/duskdolphin/script/module/moduleplatformdolphin.h b/src/duskdolphin/script/module/moduleplatformdolphin.h index 759f0f4d..2a18b7e9 100644 --- a/src/duskdolphin/script/module/moduleplatformdolphin.h +++ b/src/duskdolphin/script/module/moduleplatformdolphin.h @@ -8,6 +8,6 @@ #pragma once #include "script/scriptcontext.h" -void modulePlatformDolphin(scriptcontext_t *ctx) { - scriptContextExec(ctx, "DOLPHIN = true\n"); +static void modulePlatformDolphin(lua_State *L) { + luaL_dostring(L, "DOLPHIN = true\n"); } \ No newline at end of file diff --git a/src/dusklinux/asset/assetlinux.c b/src/dusklinux/asset/assetlinux.c index a7e93b5c..76b1a5ce 100644 --- a/src/dusklinux/asset/assetlinux.c +++ b/src/dusklinux/asset/assetlinux.c @@ -50,12 +50,20 @@ errorret_t assetInitLinux(void) { const char_t **path = ASSET_LINUX_SEARCH_PATHS; int32_t error; do { - sprintf( - searchPath, + char_t temp[ASSET_FILE_PATH_MAX]; + snprintf( + temp, + ASSET_FILE_PATH_MAX, *path, - ASSET.platform.systemPath, ASSET_FILE_NAME ); + snprintf( + searchPath, + ASSET_FILE_PATH_MAX, + "%s/%s", + ASSET.platform.systemPath, + temp + ); // Try open ASSET.zip = zip_open(searchPath, ZIP_RDONLY, &error); diff --git a/src/dusklinux/asset/assetlinux.h b/src/dusklinux/asset/assetlinux.h index 2bd7cddc..56c9f437 100644 --- a/src/dusklinux/asset/assetlinux.h +++ b/src/dusklinux/asset/assetlinux.h @@ -10,7 +10,6 @@ #include "asset/assetfile.h" static const char_t *ASSET_LINUX_SEARCH_PATHS[] = { - "%s/%s", "%s", "../%s", "../../%s", diff --git a/src/dusklinux/script/module/moduleplatformlinux.h b/src/dusklinux/script/module/moduleplatformlinux.h index 6dfd383f..eb476dac 100644 --- a/src/dusklinux/script/module/moduleplatformlinux.h +++ b/src/dusklinux/script/module/moduleplatformlinux.h @@ -8,6 +8,6 @@ #pragma once #include "script/scriptcontext.h" -void modulePlatformLinux(scriptcontext_t *ctx) { - scriptContextExec(ctx, "LINUX = true\n"); +static void modulePlatformLinux(lua_State *L) { + luaL_dostring(L, "LINUX = true\n"); } \ No newline at end of file diff --git a/src/duskpsp/script/module/moduleplatformpsp.h b/src/duskpsp/script/module/moduleplatformpsp.h index 4030e5c5..bd12bb56 100644 --- a/src/duskpsp/script/module/moduleplatformpsp.h +++ b/src/duskpsp/script/module/moduleplatformpsp.h @@ -8,6 +8,6 @@ #pragma once #include "script/scriptcontext.h" -void modulePlatformPSP(scriptcontext_t *ctx) { - scriptContextExec(ctx, "PSP = true\n"); +void modulePlatformPSP(lua_State *L) { + luaL_dostring(L, "PSP = true\n"); } \ No newline at end of file diff --git a/src/duskrpg/script/module/item/CMakeLists.txt b/src/duskrpg/script/module/item/CMakeLists.txt index 250fc610..84f36e34 100644 --- a/src/duskrpg/script/module/item/CMakeLists.txt +++ b/src/duskrpg/script/module/item/CMakeLists.txt @@ -3,8 +3,3 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - moduleitem.c -) \ No newline at end of file diff --git a/src/duskrpg/script/module/item/moduleitem.c b/src/duskrpg/script/module/item/moduleitem.c deleted file mode 100644 index 3e26dbc6..00000000 --- a/src/duskrpg/script/module/item/moduleitem.c +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleitem.h" -#include "item/inventory.h" -#include "item/backpack.h" -#include "assert/assert.h" - -void moduleItem(scriptcontext_t *context) { - assertNotNull(context, "Script context cannot be NULL"); - - // Set item information - scriptContextExec(context, ITEM_SCRIPT); - - // Bind BACKPACK const pointer - lua_pushlightuserdata(context->luaState, &BACKPACK); - lua_setglobal(context->luaState, "BACKPACK"); - - // Bind Methods - lua_register( - context->luaState, "inventoryItemExists", moduleInventoryItemExists - ); - lua_register(context->luaState, "inventoryAdd", moduleInventoryAdd); - lua_register(context->luaState, "inventorySet", moduleInventorySet); - lua_register(context->luaState, "inventoryRemove", moduleInventoryRemove); - lua_register(context->luaState, "inventoryGetCount", moduleInventoryGetCount); - lua_register(context->luaState, "inventoryIsFull", moduleInventoryIsFull); - lua_register(context->luaState, "inventoryItemFull", moduleInventoryItemFull); - lua_register(context->luaState, "inventorySort", moduleInventorySort); -} - -int moduleInventoryItemExists(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Expect inventory pointer and item ID - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "inventoryItemExists: Expected inventory pointer as first argument"); - return 0; - } - - if(!lua_isnumber(L, 2)) { - luaL_error(L, "inventoryItemExists: Expected item ID as second argument"); - return 0; - } - - inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); - itemid_t item = (itemid_t)lua_tonumber(L, 2); - - assertNotNull(inventory, "Inventory pointer cannot be NULL."); - - // Error if item is ITEM_ID_NULL - if(item == ITEM_ID_NULL) { - luaL_error(L, "inventoryItemExists: Item ID cannot be ITEM_ID_NULL"); - return 0; - } - - bool_t hasItem = inventoryItemExists(inventory, item); - lua_pushboolean(L, hasItem); - return 1; -} - -int moduleInventorySet(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Requires inventory pointer, item ID and quantity (uint8_t) - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "inventorySet: Expected inventory pointer as first argument"); - return 0; - } - - if(!lua_isnumber(L, 2)) { - luaL_error(L, "inventorySet: Expected item ID as second argument"); - return 0; - } - - if(!lua_isnumber(L, 3)) { - luaL_error(L, "inventorySet: Expected quantity as third argument"); - return 0; - } - - 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); - - assertNotNull(inventory, "Inventory pointer cannot be NULL."); - inventorySet(inventory, item, quantity); - return 0; -} - -int moduleInventoryAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Requires inventory pointer, item ID and quantity (uint8_t) - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "inventoryAdd: Expected inventory pointer as first argument"); - return 0; - } - - if(!lua_isnumber(L, 2)) { - luaL_error(L, "inventoryAdd: Expected item ID as second argument"); - return 0; - } - - if(!lua_isnumber(L, 3)) { - luaL_error(L, "inventoryAdd: Expected quantity as third argument"); - return 0; - } - - 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); - - assertNotNull(inventory, "Inventory pointer cannot be NULL."); - inventoryAdd(inventory, item, quantity); - return 0; -} - -int moduleInventoryRemove(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Requires inventory pointer and item ID - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "inventoryRemove: Expected inventory pointer as first argument"); - return 0; - } - - if(!lua_isnumber(L, 2)) { - luaL_error(L, "inventoryRemove: Expected item ID as second argument"); - return 0; - } - - inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); - itemid_t item = (itemid_t)lua_tonumber(L, 2); - assertNotNull(inventory, "Inventory pointer cannot be NULL."); - - // if there is a third argument (quantity), then we are actually doing a - // partial removal. - if(lua_gettop(L) >= 3) { - if(!lua_isnumber(L, 3)) { - luaL_error(L, "inventoryRemove: Expected quantity as third argument"); - return 0; - } - uint8_t amount = (uint8_t)lua_tonumber(L, 3); - uint8_t currentQuantity = inventoryGetCount(inventory, item); - if(amount >= currentQuantity) { - // Remove entire stack - inventoryRemove(inventory, item); - return 0; - } - - // Set new quantity - inventorySet(inventory, item, currentQuantity - amount); - return 0; - } - - inventoryRemove(inventory, item); - return 0; -} - -int moduleInventoryGetCount(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Requires inventory pointer and item ID - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "inventoryGetCount: Expected inventory pointer as first argument"); - return 0; - } - - if(!lua_isnumber(L, 2)) { - luaL_error(L, "inventoryGetCount: Expected item ID as second argument"); - return 0; - } - - inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); - itemid_t item = (itemid_t)lua_tonumber(L, 2); - - assertNotNull(inventory, "Inventory pointer cannot be NULL."); - - uint8_t count = inventoryGetCount(inventory, item); - lua_pushnumber(L, count); - return 1; -} - -int moduleInventoryIsFull(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Requires inventory pointer - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "inventoryIsFull: Expected inventory pointer as first argument"); - return 0; - } - - inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); - - assertNotNull(inventory, "Inventory pointer cannot be NULL."); - - bool_t isFull = inventoryIsFull(inventory); - lua_pushboolean(L, isFull); - return 1; -} - -int moduleInventoryItemFull(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Requires inventory pointer and item ID - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "inventoryItemFull: Expected inventory pointer as first argument"); - return 0; - } - - if(!lua_isnumber(L, 2)) { - luaL_error(L, "inventoryItemFull: Expected item ID as second argument"); - return 0; - } - - inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); - itemid_t item = (itemid_t)lua_tonumber(L, 2); - - assertNotNull(inventory, "Inventory pointer cannot be NULL."); - - bool_t isFull = inventoryItemFull(inventory, item); - lua_pushboolean(L, isFull); - return 1; -} - -int moduleInventorySort(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Requires inventory pointer, sort type and reverse flag - if(!lua_islightuserdata(L, 1)) { - luaL_error(L, "inventorySort: Expected inventory pointer as first argument"); - return 0; - } - - if(!lua_isnumber(L, 2)) { - luaL_error(L, "inventorySort: Expected sort type as second argument"); - return 0; - } - - // Optional, reverse - 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; - } - - reverse = (bool_t)lua_toboolean(L, 3); - } - - inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); - inventorysort_t sortBy = (inventorysort_t)lua_tonumber(L, 2); - - assertNotNull(inventory, "Inventory pointer cannot be NULL."); - inventorySort(inventory, sortBy, reverse); - return 0; -} - diff --git a/src/duskrpg/script/module/item/moduleitem.h b/src/duskrpg/script/module/item/moduleitem.h index 91b0ac97..f7cf67db 100644 --- a/src/duskrpg/script/module/item/moduleitem.h +++ b/src/duskrpg/script/module/item/moduleitem.h @@ -1,80 +1,241 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once #include "script/scriptcontext.h" +#include "item/inventory.h" +#include "item/backpack.h" +#include "assert/assert.h" -/** - * Register item functions to the given script context. - * - * @param context The script context to register item functions to. - */ -void moduleItem(scriptcontext_t *context); +static int moduleInventoryItemExists(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); -/** - * Script binding for checking if an item exists in an inventory. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInventoryItemExists(lua_State *L); + if(!lua_islightuserdata(L, 1)) { + luaL_error(L, "inventoryItemExists: Expected inventory pointer as first argument"); + return 0; + } -/** - * Script binding for adding an item to an inventory. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInventorySet(lua_State *L); + if(!lua_isnumber(L, 2)) { + luaL_error(L, "inventoryItemExists: Expected item ID as second argument"); + return 0; + } -/** - * Script binding for setting the quantity of an item in an inventory. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInventoryAdd(lua_State *L); + inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); + itemid_t item = (itemid_t)lua_tonumber(L, 2); -/** - * Script binding for removing an item from an inventory. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInventoryRemove(lua_State *L); + assertNotNull(inventory, "Inventory pointer cannot be NULL."); -/** - * Script binding for getting the count of an item in an inventory. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInventoryGetCount(lua_State *L); + if(item == ITEM_ID_NULL) { + luaL_error(L, "inventoryItemExists: Item ID cannot be ITEM_ID_NULL"); + return 0; + } -/** - * Script binding for checking if an inventory is full. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInventoryIsFull(lua_State *L); + bool_t hasItem = inventoryItemExists(inventory, item); + lua_pushboolean(L, hasItem); + return 1; +} -/** - * Script binding for checking if an item stack in an inventory is full. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInventoryItemFull(lua_State *L); +static int moduleInventorySet(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); -/** - * Script binding for sorting an inventory. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleInventorySort(lua_State *L); + if(!lua_islightuserdata(L, 1)) { + luaL_error(L, "inventorySet: Expected inventory pointer as first argument"); + return 0; + } + + if(!lua_isnumber(L, 2)) { + luaL_error(L, "inventorySet: Expected item ID as second argument"); + return 0; + } + + if(!lua_isnumber(L, 3)) { + luaL_error(L, "inventorySet: Expected quantity as third argument"); + return 0; + } + + 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); + + assertNotNull(inventory, "Inventory pointer cannot be NULL."); + inventorySet(inventory, item, quantity); + return 0; +} + +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; + } + + if(!lua_isnumber(L, 2)) { + luaL_error(L, "inventoryAdd: Expected item ID as second argument"); + return 0; + } + + if(!lua_isnumber(L, 3)) { + luaL_error(L, "inventoryAdd: Expected quantity as third argument"); + return 0; + } + + 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); + + assertNotNull(inventory, "Inventory pointer cannot be NULL."); + inventoryAdd(inventory, item, quantity); + return 0; +} + +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; + } + + if(!lua_isnumber(L, 2)) { + luaL_error(L, "inventoryRemove: Expected item ID as second argument"); + return 0; + } + + inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); + itemid_t item = (itemid_t)lua_tonumber(L, 2); + 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; + } + uint8_t amount = (uint8_t)lua_tonumber(L, 3); + uint8_t currentQuantity = inventoryGetCount(inventory, item); + if(amount >= currentQuantity) { + inventoryRemove(inventory, item); + return 0; + } + inventorySet(inventory, item, currentQuantity - amount); + return 0; + } + + inventoryRemove(inventory, item); + return 0; +} + +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; + } + + if(!lua_isnumber(L, 2)) { + luaL_error(L, "inventoryGetCount: Expected item ID as second argument"); + return 0; + } + + inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); + itemid_t item = (itemid_t)lua_tonumber(L, 2); + + assertNotNull(inventory, "Inventory pointer cannot be NULL."); + + uint8_t count = inventoryGetCount(inventory, item); + lua_pushnumber(L, count); + return 1; +} + +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; + } + + inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); + + assertNotNull(inventory, "Inventory pointer cannot be NULL."); + + bool_t isFull = inventoryIsFull(inventory); + lua_pushboolean(L, isFull); + return 1; +} + +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; + } + + if(!lua_isnumber(L, 2)) { + luaL_error(L, "inventoryItemFull: Expected item ID as second argument"); + return 0; + } + + inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); + itemid_t item = (itemid_t)lua_tonumber(L, 2); + + assertNotNull(inventory, "Inventory pointer cannot be NULL."); + + bool_t isFull = inventoryItemFull(inventory, item); + lua_pushboolean(L, isFull); + return 1; +} + +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; + } + + if(!lua_isnumber(L, 2)) { + luaL_error(L, "inventorySort: Expected sort type as second argument"); + return 0; + } + + 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; + } + reverse = (bool_t)lua_toboolean(L, 3); + } + + inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); + inventorysort_t sortBy = (inventorysort_t)lua_tonumber(L, 2); + + assertNotNull(inventory, "Inventory pointer cannot be NULL."); + inventorySort(inventory, sortBy, reverse); + return 0; +} + +static void moduleItem(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + luaL_dostring(L, ITEM_SCRIPT); + + 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); +} diff --git a/src/duskrpg/script/module/story/CMakeLists.txt b/src/duskrpg/script/module/story/CMakeLists.txt index 6496420c..84f36e34 100644 --- a/src/duskrpg/script/module/story/CMakeLists.txt +++ b/src/duskrpg/script/module/story/CMakeLists.txt @@ -3,8 +3,3 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - modulestoryflag.c -) \ No newline at end of file diff --git a/src/duskrpg/script/module/story/modulestoryflag.c b/src/duskrpg/script/module/story/modulestoryflag.c deleted file mode 100644 index c0dc6659..00000000 --- a/src/duskrpg/script/module/story/modulestoryflag.c +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulestoryflag.h" -#include "assert/assert.h" -#include "story/storyflag.h" - -void moduleStoryFlag(scriptcontext_t *context) { - assertNotNull(context, "Script context cannot be NULL"); - - lua_register(context->luaState, "storyFlagGet", moduleStoryFlagGet); - lua_register(context->luaState, "storyFlagSet", moduleStoryFlagSet); - lua_register( - context->luaState, "storyFlagIncrement", moduleStoryFlagIncrement - ); - lua_register( - context->luaState, "storyFlagDecrement", moduleStoryFlagDecrement - ); -} - -int moduleStoryFlagGet(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Require story flag ID argument - if(!lua_isnumber(L, 1)) { - luaL_error(L, "Expected flag ID."); - return 0; - } - - storyflag_t flag = (storyflag_t)lua_tonumber(L, 1); - if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { - luaL_error(L, "Invalid flag ID %d", flag); - return 0; - } - - storyflagvalue_t value = storyFlagGet(flag); - lua_pushnumber(L, value); - return 1; -} - -int moduleStoryFlagSet(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Require story flag ID argument - if(!lua_isnumber(L, 1)) { - luaL_error(L, "Expected flag ID."); - return 0; - } - - // Require story flag value argument - if(!lua_isnumber(L, 2)) { - luaL_error(L, "Expected flag value."); - return 0; - } - - storyflag_t flag = (storyflag_t)lua_tonumber(L, 1); - if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { - luaL_error(L, "Invalid flag ID %d", flag); - return 0; - } - - storyflagvalue_t value = (storyflagvalue_t)lua_tonumber(L, 2); - storyFlagSet(flag, value); - return 0; -} - -int moduleStoryFlagIncrement(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Require story flag ID argument - if(!lua_isnumber(L, 1)) { - luaL_error(L, "Expected flag ID."); - return 0; - } - - storyflag_t flag = (storyflag_t)lua_tonumber(L, 1); - if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { - luaL_error(L, "Invalid flag ID %d", flag); - return 0; - } - - storyflagvalue_t value = storyFlagGet(flag); - storyFlagSet(flag, value + 1); - return 0; -} - -int moduleStoryFlagDecrement(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - // Require story flag ID argument - if(!lua_isnumber(L, 1)) { - luaL_error(L, "Expected flag ID."); - return 0; - } - - storyflag_t flag = (storyflag_t)lua_tonumber(L, 1); - if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { - luaL_error(L, "Invalid flag ID %d", flag); - return 0; - } - - storyflagvalue_t value = storyFlagGet(flag); - storyFlagSet(flag, value - 1); - return 0; -} \ No newline at end of file diff --git a/src/duskrpg/script/module/story/modulestoryflag.h b/src/duskrpg/script/module/story/modulestoryflag.h index c398c3e3..9218a2b2 100644 --- a/src/duskrpg/script/module/story/modulestoryflag.h +++ b/src/duskrpg/script/module/story/modulestoryflag.h @@ -1,48 +1,101 @@ /** * Copyright (c) 2025 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once #include "script/scriptcontext.h" +#include "assert/assert.h" +#include "story/storyflag.h" -/** - * Register story flag module functions to the given script context. - * - * @param context The script context to register story flag module functions to. - */ -void moduleStoryFlag(scriptcontext_t *context); +static int moduleStoryFlagGet(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); -/** - * Script binding for getting the value of a story flag. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleStoryFlagGet(lua_State *L); + if(!lua_isnumber(L, 1)) { + luaL_error(L, "Expected flag ID."); + return 0; + } -/** - * Script binding for setting the value of a story flag. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleStoryFlagSet(lua_State *L); + storyflag_t flag = (storyflag_t)lua_tonumber(L, 1); + if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { + luaL_error(L, "Invalid flag ID %d", flag); + return 0; + } -/** - * Script binding for incrementing a story flag. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleStoryFlagIncrement(lua_State *L); + storyflagvalue_t value = storyFlagGet(flag); + lua_pushnumber(L, value); + return 1; +} -/** - * Script binding for decrementing a story flag. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleStoryFlagDecrement(lua_State *L); \ No newline at end of file +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); + if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { + luaL_error(L, "Invalid flag ID %d", flag); + return 0; + } + + storyflagvalue_t value = (storyflagvalue_t)lua_tonumber(L, 2); + storyFlagSet(flag, value); + return 0; +} + +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); + if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { + luaL_error(L, "Invalid flag ID %d", flag); + return 0; + } + + 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); + if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { + luaL_error(L, "Invalid flag ID %d", flag); + return 0; + } + + 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); +} diff --git a/src/duskrpg/script/scriptgame.h b/src/duskrpg/script/scriptgame.h index de516489..16094648 100644 --- a/src/duskrpg/script/scriptgame.h +++ b/src/duskrpg/script/scriptgame.h @@ -9,6 +9,6 @@ #include "script/module/item/moduleitem.h" #include "script/module/story/modulestoryflag.h" -#define SCRIPT_GAME_LIST \ - { .name = "item", .callback = moduleItem }, \ - { .name = "storyflag", .callback = moduleStoryFlag }, \ No newline at end of file +#define SCRIPT_GAME_INIT(L) \ + moduleItem(L); \ + moduleStoryFlag(L); \ No newline at end of file