Playertest: scene/script system refactor and Wii ABI fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 23:30:08 -05:00
parent 7c3386cf3e
commit 998601f722
104 changed files with 2855 additions and 3923 deletions
+25
View File
@@ -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
+1 -7
View File
@@ -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')
Scene.set('scenes/cube.lua')
+30
View File
@@ -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
+1 -1
View File
@@ -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)
@@ -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);
+2 -5
View File
@@ -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();
}
@@ -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"
@@ -93,3 +95,37 @@ void entityCameraGetProjection(
);
}
}
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;
}
@@ -97,3 +97,26 @@ void entityCameraSetZFar(
const componentid_t comp,
const float_t zFar
);
/**
* 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);
@@ -49,3 +49,14 @@ void entityMaterialSetShader(
);
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;
}
@@ -61,3 +61,16 @@ void entityMaterialSetShader(
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
);
+103 -1
View File
@@ -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(
@@ -39,3 +45,99 @@ void entityMeshSetMesh(
);
comp->mesh = mesh;
}
void entityMeshDispose(
const entityid_t entityId,
const componentid_t componentId
) {
entitymesh_t *comp = componentGetData(
entityId, componentId, COMPONENT_TYPE_MESH
);
if(!comp->ownsData) return;
(void)meshDispose(&comp->ownedMesh);
memoryFree(comp->ownedVertices);
comp->ownedVertices = NULL;
comp->ownsData = false;
comp->mesh = NULL;
}
errorret_t entityMeshGeneratePlane(
const entityid_t entityId,
const componentid_t componentId,
const float_t width,
const float_t height
) {
entitymesh_t *comp = componentGetData(
entityId, componentId, COMPONENT_TYPE_MESH
);
entityMeshDispose(entityId, componentId);
comp->ownedVertices = memoryAllocate(PLANE_VERTEX_COUNT * sizeof(meshvertex_t));
assertNotNull(comp->ownedVertices, "Failed to allocate plane vertices");
vec3 min = { -width * 0.5f, 0.0f, -height * 0.5f };
vec3 max = { width * 0.5f, 0.0f, height * 0.5f };
vec2 uvMin = { 0.0f, 0.0f };
vec2 uvMax = { 1.0f, 1.0f };
planeBuffer(
comp->ownedVertices,
PLANE_AXIS_XZ,
min,
max
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
, uvMin,
uvMax
);
errorChain(meshInit(
&comp->ownedMesh,
PLANE_PRIMITIVE_TYPE,
PLANE_VERTEX_COUNT,
comp->ownedVertices
));
comp->mesh = &comp->ownedMesh;
comp->ownsData = true;
errorOk();
}
errorret_t entityMeshGenerateCapsule(
const entityid_t entityId,
const componentid_t componentId,
const float_t radius,
const float_t halfHeight
) {
entitymesh_t *comp = componentGetData(
entityId, componentId, COMPONENT_TYPE_MESH
);
entityMeshDispose(entityId, componentId);
comp->ownedVertices = memoryAllocate(CAPSULE_VERTEX_COUNT * sizeof(meshvertex_t));
assertNotNull(comp->ownedVertices, "Failed to allocate capsule vertices");
vec3 center = { 0.0f, 0.0f, 0.0f };
capsuleBuffer(
comp->ownedVertices,
center,
radius,
halfHeight,
CAPSULE_CAP_RINGS,
CAPSULE_SECTORS
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&comp->ownedMesh,
CAPSULE_PRIMITIVE_TYPE,
CAPSULE_VERTEX_COUNT,
comp->ownedVertices
));
comp->mesh = &comp->ownedMesh;
comp->ownsData = true;
errorOk();
}
@@ -11,6 +11,9 @@
typedef struct {
mesh_t *mesh;
mesh_t ownedMesh;
meshvertex_t *ownedVertices;
bool_t ownsData;
} entitymesh_t;
/**
@@ -48,3 +51,46 @@ void entityMeshSetMesh(
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
);
@@ -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
@@ -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.
@@ -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
)
@@ -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);
}
@@ -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
);
+1 -3
View File
@@ -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)
+43 -34
View File
@@ -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");
}
+20 -20
View File
@@ -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) {
+91 -65
View File
@@ -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,25 +82,12 @@ 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();
}
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)
);
}
if(SCENE.scriptActive) {
errorChain(sceneCall("update"));
}
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
);
}
@@ -245,3 +259,15 @@ errorret_t sceneDispose(void) {
errorChain(sceneSetImmediate(NULL));
errorOk();
}
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;
}
+7 -38
View File
@@ -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 sceneSetImmediate(const char_t *scene);
void sceneSet(const char_t *scene);
errorret_t sceneDispose(void);
int sceneSetLua(lua_State *L);
-2
View File
@@ -8,8 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
scriptmanager.c
scriptcontext.c
scriptmodule.c
)
# Subdirectories
add_subdirectory(module)
-15
View File
@@ -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)
@@ -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
)
@@ -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;
}
+107 -41
View File
@@ -6,50 +6,116 @@
*/
#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);
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);
}
-283
View File
@@ -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;
}
@@ -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);
@@ -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;
}
+30 -28
View File
@@ -6,35 +6,37 @@
*/
#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);
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);
}
@@ -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;
}
+77 -29
View File
@@ -6,37 +6,85 @@
*/
#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();
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);
}
@@ -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;
}
@@ -6,35 +6,66 @@
*/
#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);
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);
}
@@ -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;
}
+61 -21
View File
@@ -6,27 +6,67 @@
*/
#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);
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);
}
@@ -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;
}
+89 -6
View File
@@ -6,10 +6,93 @@
*/
#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);
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);
}
@@ -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;
}
+94 -42
View File
@@ -6,51 +6,103 @@
*/
#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);
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);
}
@@ -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)
@@ -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
)
@@ -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;
}
@@ -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);
@@ -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;
}
@@ -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);
@@ -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;
}
@@ -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;
}
@@ -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);
@@ -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;
}
+331 -21
View File
@@ -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);
}
@@ -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
)
@@ -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;
}
@@ -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);
@@ -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
)
@@ -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;
}
@@ -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);
@@ -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
)
@@ -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);
}
+48 -11
View File
@@ -6,16 +6,53 @@
*/
#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);
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);
}
@@ -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
)
-217
View File
@@ -1,217 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#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;
}
+190 -55
View File
@@ -6,66 +6,201 @@
*/
#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);
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);
}
@@ -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
)
@@ -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;
}
+75 -14
View File
@@ -6,19 +6,80 @@
*/
#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);
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);
}
+117
View File
@@ -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);
}
+21
View File
@@ -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);
}
+169
View File
@@ -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);
}
+180
View File
@@ -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);
}
+168
View File
@@ -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);
}
+45
View File
@@ -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);
}
@@ -7,7 +7,6 @@
#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"
+4 -4
View File
@@ -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
}
@@ -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
)
@@ -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;
}
@@ -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);
@@ -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);
}
@@ -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
)
@@ -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;
}
@@ -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);
@@ -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
)
-41
View File
@@ -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;
}
+30 -14
View File
@@ -6,20 +6,36 @@
*/
#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);
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");
}
-10
View File
@@ -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
)
-15
View File
@@ -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");
}
+4 -8
View File
@@ -6,12 +6,8 @@
*/
#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);
static void moduleUi(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
}
+12 -7
View File
@@ -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");
@@ -28,12 +32,13 @@ 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();
}
+4
View File
@@ -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();
}
-79
View File
@@ -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;
}
-24
View File
@@ -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);
@@ -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");
}
+11 -3
View File
@@ -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);
-1
View File
@@ -10,7 +10,6 @@
#include "asset/assetfile.h"
static const char_t *ASSET_LINUX_SEARCH_PATHS[] = {
"%s/%s",
"%s",
"../%s",
"../../%s",
@@ -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");
}
@@ -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");
}
@@ -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
)
-262
View File
@@ -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;
}
+223 -62
View File
@@ -7,74 +7,235 @@
#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);
}

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