Module updating

This commit is contained in:
2026-04-14 16:36:50 -05:00
parent 46a5403511
commit 2a9667feca
28 changed files with 1985 additions and 20 deletions
+14
View File
@@ -0,0 +1,14 @@
module('entity')
module('entityposition')
module('entitymaterial')
module('glm')
module('color')
-- Position
local posComp = entityAddComponent(ENTITY_ID, COMPONENT_TYPE_POSITION)
entityPositionSetPosition(ENTITY_ID, posComp, vec3(1, 2, 3))
-- Material
local matComp = entityAddComponent(ENTITY_ID, COMPONENT_TYPE_MATERIAL)
local material = entityMaterialGetShaderMaterial(ENTITY_ID, matComp)
material.unlit.color = COLOR_BLUE
+5 -17
View File
@@ -81,19 +81,14 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
floorPhysData->shape.data.plane.normal[2] = 0.0f; floorPhysData->shape.data.plane.normal[2] = 0.0f;
floorPhysData->shape.data.plane.distance = 0.0f; floorPhysData->shape.data.plane.distance = 0.0f;
/* ---- Dynamic box ---- */ // Test Box
phBoxEnt = entityManagerAdd(); phBoxEnt = entityManagerAdd();
componentid_t boxPos = entityAddComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
componentid_t boxMesh = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MESH); componentid_t boxMesh = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MESH);
componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL); // componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL);
phBoxPhys = entityAddComponent(phBoxEnt, COMPONENT_TYPE_PHYSICS);
entityMeshSetMesh(phBoxEnt, boxMesh, &CUBE_MESH_SIMPLE); entityMeshSetMesh(phBoxEnt, boxMesh, &CUBE_MESH_SIMPLE);
entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED; // entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED;
componentid_t boxScript = entityAddComponent(phBoxEnt, COMPONENT_TYPE_SCRIPT);
/* Physics position lives in the POSITION component. CUBE_MESH_SIMPLE is errorChain(entityScriptExecAsset(phBoxEnt, boxScript, "entity/test.lua"));
* centred at origin (-0.5..0.5), so entity position == physics centre. */
entityPositionSetPosition(phBoxEnt, boxPos, (vec3){ 0.0f, 4.0f, 0.0f });
/* Run the init script. */ /* Run the init script. */
scriptcontext_t ctx; scriptcontext_t ctx;
@@ -111,13 +106,6 @@ errorret_t engineUpdate(void) {
uiUpdate(); uiUpdate();
errorChain(sceneUpdate()); errorChain(sceneUpdate());
/* Reset the box to its start position on demand. */
if(inputIsDown(INPUT_ACTION_ACCEPT)) {
componentid_t posComp = entityGetComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
entityPositionSetPosition(phBoxEnt, posComp, (vec3){ 0.0f, 4.0f, 0.0f });
entityPhysicsSetVelocity(phBoxEnt, phBoxPhys, (vec3){ 0.0f, 0.0f, 0.0f });
}
/* Step physics: positions are updated directly on POSITION components. */ /* Step physics: positions are updated directly on POSITION components. */
physicsManagerUpdate(); physicsManagerUpdate();
+2 -1
View File
@@ -4,4 +4,5 @@
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_subdirectory(display) add_subdirectory(display)
add_subdirectory(physics) add_subdirectory(physics)
add_subdirectory(script)
@@ -55,6 +55,65 @@ void entityCameraSetZFar(
cam->farClip = zFar; cam->farClip = zFar;
} }
entitycameraprojectiontype_t entityCameraGetProjType(
const entityid_t ent,
const componentid_t comp
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
return cam->projType;
}
void entityCameraSetProjType(
const entityid_t ent,
const componentid_t comp,
const entitycameraprojectiontype_t type
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->projType = type;
}
float_t entityCameraGetFov(
const entityid_t ent,
const componentid_t comp
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
return cam->perspective.fov;
}
void entityCameraSetFov(
const entityid_t ent,
const componentid_t comp,
const float_t fov
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->perspective.fov = fov;
}
void entityCameraSetOrthographic(
const entityid_t ent,
const componentid_t comp,
const float_t left,
const float_t right,
const float_t top,
const float_t bottom
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->orthographic.left = left;
cam->orthographic.right = right;
cam->orthographic.top = top;
cam->orthographic.bottom = bottom;
}
void entityCameraGetProjection( void entityCameraGetProjection(
const entityid_t ent, const entityid_t ent,
const componentid_t comp, const componentid_t comp,
@@ -96,4 +96,73 @@ void entityCameraSetZFar(
const entityid_t ent, const entityid_t ent,
const componentid_t comp, const componentid_t comp,
const float_t zFar const float_t zFar
);
/**
* Gets the projection type of a camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @return The projection type.
*/
entitycameraprojectiontype_t entityCameraGetProjType(
const entityid_t ent,
const componentid_t comp
);
/**
* Sets the projection type of a camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @param type The projection type.
*/
void entityCameraSetProjType(
const entityid_t ent,
const componentid_t comp,
const entitycameraprojectiontype_t type
);
/**
* Gets the field of view (in radians) of a perspective camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @return The field of view in radians.
*/
float_t entityCameraGetFov(
const entityid_t ent,
const componentid_t comp
);
/**
* Sets the field of view (in radians) of a perspective camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @param fov The field of view in radians.
*/
void entityCameraSetFov(
const entityid_t ent,
const componentid_t comp,
const float_t fov
);
/**
* Sets the orthographic projection bounds of a camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @param left Left bound.
* @param right Right bound.
* @param top Top bound.
* @param bottom Bottom bound.
*/
void entityCameraSetOrthographic(
const entityid_t ent,
const componentid_t comp,
const float_t left,
const float_t right,
const float_t top,
const float_t bottom
); );
@@ -100,6 +100,44 @@ bool_t entityPhysicsIsOnGround(
return phys->onGround; return phys->onGround;
} }
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 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;
}
float_t entityPhysicsGetGravityScale(
const entityid_t entityId,
const componentid_t componentId
) {
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
assertNotNull(phys, "Failed to get physics component data");
return phys->gravityScale;
}
void entityPhysicsSetGravityScale(
const entityid_t entityId,
const componentid_t componentId,
const float_t scale
) {
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
assertNotNull(phys, "Failed to get physics component data");
phys->gravityScale = scale;
}
void entityPhysicsDispose( void entityPhysicsDispose(
const entityid_t entityId, const entityid_t entityId,
const componentid_t componentId const componentid_t componentId
@@ -122,6 +122,56 @@ bool_t entityPhysicsIsOnGround(
const componentid_t componentId const componentid_t componentId
); );
/**
* Gets the body type of the entity's physics body.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @return The body type (static, dynamic, kinematic).
*/
physicsbodytype_t entityPhysicsGetBodyType(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Sets the body type of the entity's physics body.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param type The new body type.
*/
void entityPhysicsSetBodyType(
const entityid_t entityId,
const componentid_t componentId,
const physicsbodytype_t type
);
/**
* Gets the gravity scale of the entity's physics body.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @return The gravity scale factor.
*/
float_t entityPhysicsGetGravityScale(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Sets the gravity scale of the entity's physics body.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param scale The new gravity scale factor.
*/
void entityPhysicsSetGravityScale(
const entityid_t entityId,
const componentid_t componentId,
const float_t scale
);
/** /**
* Releases the body slot back to PHYSICS_WORLD. Called automatically when * Releases the body slot back to PHYSICS_WORLD. Called automatically when
* the component is disposed via the component system. * the component is disposed via the component system.
@@ -0,0 +1,10 @@
# 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
entityscript.c
)
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entityscript.h"
#include "entity/component.h"
#include "assert/assert.h"
void entityScriptInit(
const entityid_t entityId,
const componentid_t componentId
) {
entityscript_t *script = (entityscript_t*)componentGetData(
entityId, componentId, COMPONENT_TYPE_SCRIPT
);
scriptContextInit(&script->scriptContext);
// Define script globals.
char_t strScript[64];
snprintf(strScript, sizeof(strScript), "ENTITY_ID = %d\n", entityId);
errorret_t ret = scriptContextExec(&script->scriptContext, strScript);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
assertUnreachable("Failed to set up script globals");
}
}
errorret_t entityScriptExec(
const entityid_t entityId,
const componentid_t componentId,
const char_t *script
) {
entityscript_t *entityScript = (entityscript_t*)componentGetData(
entityId, componentId, COMPONENT_TYPE_SCRIPT
);
return scriptContextExec(&entityScript->scriptContext, script);
}
errorret_t entityScriptExecAsset(
const entityid_t entityId,
const componentid_t componentId,
const char_t *assetName
) {
entityscript_t *entityScript = (entityscript_t*)componentGetData(
entityId, componentId, COMPONENT_TYPE_SCRIPT
);
return scriptContextExecFile(&entityScript->scriptContext, assetName);
}
void entityScriptDispose(
const entityid_t entityId,
const componentid_t componentId
) {
entityscript_t *script = (entityscript_t*)componentGetData(
entityId, componentId, COMPONENT_TYPE_SCRIPT
);
scriptContextDispose(&script->scriptContext);
}
@@ -0,0 +1,64 @@
/**
* 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 scriptContext;
} entityscript_t;
/**
* Initializes the script entity component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
*/
void entityScriptInit(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Executes a script on the entity's script component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param script The script to execute.
* @return The error return value.
*/
errorret_t entityScriptExec(
const entityid_t entityId,
const componentid_t componentId,
const char_t *script
);
/**
* Executes a script from an asset on the entity's script component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param assetName The name of the script asset to execute.
* @return The error return value.
*/
errorret_t entityScriptExecAsset(
const entityid_t entityId,
const componentid_t componentId,
const char_t *assetName
);
/**
* Disposes of the script entity component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
*/
void entityScriptDispose(
const entityid_t entityId,
const componentid_t componentId
);
+3 -1
View File
@@ -10,9 +10,11 @@
#include "entity/component/display/entitymesh.h" #include "entity/component/display/entitymesh.h"
#include "entity/component/display/entitymaterial.h" #include "entity/component/display/entitymaterial.h"
#include "entity/component/physics/entityphysics.h" #include "entity/component/physics/entityphysics.h"
#include "entity/component/script/entityscript.h"
X(POSITION, entityposition_t, position, entityPositionInit, NULL) X(POSITION, entityposition_t, position, entityPositionInit, NULL)
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL) X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
X(MESH, entitymesh_t, mesh, entityMeshInit, NULL) X(MESH, entitymesh_t, mesh, entityMeshInit, NULL)
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL) X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose) X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
X(SCRIPT, entityscript_t, script, entityScriptInit, entityScriptDispose)
+2 -1
View File
@@ -11,4 +11,5 @@ add_subdirectory(locale)
add_subdirectory(system) add_subdirectory(system)
add_subdirectory(scene) add_subdirectory(scene)
add_subdirectory(time) add_subdirectory(time)
add_subdirectory(ui) add_subdirectory(ui)
add_subdirectory(entity)
@@ -0,0 +1,16 @@
# 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
moduleentityposition.c
moduleentitycamera.c
moduleentitymesh.c
moduleentitymaterial.c
moduleentityphysics.c
moduleentityscript.c
)
@@ -0,0 +1,108 @@
/**
* 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 "entity/component.h"
void moduleEntity(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
// Component type constants
lua_pushnumber(l, COMPONENT_TYPE_POSITION);
lua_setglobal(l, "COMPONENT_TYPE_POSITION");
lua_pushnumber(l, COMPONENT_TYPE_CAMERA);
lua_setglobal(l, "COMPONENT_TYPE_CAMERA");
lua_pushnumber(l, COMPONENT_TYPE_MESH);
lua_setglobal(l, "COMPONENT_TYPE_MESH");
lua_pushnumber(l, COMPONENT_TYPE_MATERIAL);
lua_setglobal(l, "COMPONENT_TYPE_MATERIAL");
lua_pushnumber(l, COMPONENT_TYPE_PHYSICS);
lua_setglobal(l, "COMPONENT_TYPE_PHYSICS");
lua_pushnumber(l, COMPONENT_TYPE_SCRIPT);
lua_setglobal(l, "COMPONENT_TYPE_SCRIPT");
// Entity functions
lua_register(l, "entityCreate", moduleEntityCreate);
lua_register(l, "entityAddComponent", moduleEntityAddComponent);
lua_register(l, "entityGetComponent", moduleEntityGetComponent);
lua_register(l, "entityDispose", moduleEntityDispose);
}
int moduleEntityCreate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entityid_t id = entityManagerAdd();
entityInit(id);
lua_pushnumber(L, id);
return 1;
}
int moduleEntityAddComponent(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityAddComponent: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityAddComponent: componentType must be a number");
return 0;
}
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
componenttype_t type = (componenttype_t)lua_tonumber(L, 2);
componentid_t compId = entityAddComponent(entityId, type);
lua_pushnumber(L, compId);
return 1;
}
int moduleEntityGetComponent(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityGetComponent: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityGetComponent: componentType must be a number");
return 0;
}
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
componenttype_t type = (componenttype_t)lua_tonumber(L, 2);
componentid_t compId = entityGetComponent(entityId, type);
if(compId == 0xFF) {
lua_pushnil(L);
} else {
lua_pushnumber(L, compId);
}
return 1;
}
int moduleEntityDispose(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityDispose: entityId must be a number");
return 0;
}
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
entityDispose(entityId);
return 0;
}
@@ -0,0 +1,44 @@
/**
* 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 entity module within the given script context.
* Provides: entityCreate, entityAddComponent, entityGetComponent, entityDispose
* and all COMPONENT_TYPE_* constants.
*
* @param ctx The script context to register the module in.
*/
void moduleEntity(scriptcontext_t *ctx);
/**
* Lua binding: reserves and initializes a new entity.
* Returns: entityId (integer)
*/
int moduleEntityCreate(lua_State *L);
/**
* Lua binding: adds a component of the given type to an entity.
* Args: entityId (integer), componentType (integer)
* Returns: componentId (integer)
*/
int moduleEntityAddComponent(lua_State *L);
/**
* Lua binding: gets the component ID for the given type on an entity.
* Args: entityId (integer), componentType (integer)
* Returns: componentId (integer), or nil if the entity lacks the component
*/
int moduleEntityGetComponent(lua_State *L);
/**
* Lua binding: disposes of the entity with the given ID.
* Args: entityId (integer)
*/
int moduleEntityDispose(lua_State *L);
@@ -0,0 +1,228 @@
/**
* 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/component/display/entitycamera.h"
void moduleEntityCamera(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
// Projection type constants
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE);
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_PERSPECTIVE");
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED);
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_PERSPECTIVE_FLIPPED");
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC);
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_ORTHOGRAPHIC");
// Camera functions
lua_register(l, "entityCameraGetZNear", moduleEntityCameraGetZNear);
lua_register(l, "entityCameraSetZNear", moduleEntityCameraSetZNear);
lua_register(l, "entityCameraGetZFar", moduleEntityCameraGetZFar);
lua_register(l, "entityCameraSetZFar", moduleEntityCameraSetZFar);
lua_register(l, "entityCameraGetProjType", moduleEntityCameraGetProjType);
lua_register(l, "entityCameraSetProjType", moduleEntityCameraSetProjType);
lua_register(l, "entityCameraGetFov", moduleEntityCameraGetFov);
lua_register(l, "entityCameraSetFov", moduleEntityCameraSetFov);
lua_register(l, "entityCameraSetOrthographic", moduleEntityCameraSetOrthographic);
}
int moduleEntityCameraGetZNear(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraGetZNear: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraGetZNear: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityCameraGetZNear(eid, cid));
return 1;
}
int moduleEntityCameraSetZNear(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetZNear: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetZNear: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityCameraSetZNear: zNear must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t zNear = (float_t)lua_tonumber(L, 3);
entityCameraSetZNear(eid, cid, zNear);
return 0;
}
int moduleEntityCameraGetZFar(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraGetZFar: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraGetZFar: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityCameraGetZFar(eid, cid));
return 1;
}
int moduleEntityCameraSetZFar(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetZFar: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetZFar: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityCameraSetZFar: zFar must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t zFar = (float_t)lua_tonumber(L, 3);
entityCameraSetZFar(eid, cid, zFar);
return 0;
}
int moduleEntityCameraGetProjType(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraGetProjType: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraGetProjType: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityCameraGetProjType(eid, cid));
return 1;
}
int moduleEntityCameraSetProjType(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetProjType: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetProjType: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityCameraSetProjType: projType must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
entitycameraprojectiontype_t type = (entitycameraprojectiontype_t)lua_tonumber(L, 3);
entityCameraSetProjType(eid, cid, type);
return 0;
}
int moduleEntityCameraGetFov(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraGetFov: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraGetFov: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityCameraGetFov(eid, cid));
return 1;
}
int moduleEntityCameraSetFov(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetFov: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetFov: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityCameraSetFov: fov must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t fov = (float_t)lua_tonumber(L, 3);
entityCameraSetFov(eid, cid, fov);
return 0;
}
int moduleEntityCameraSetOrthographic(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetOrthographic: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetOrthographic: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3) || !lua_isnumber(L, 4) ||
!lua_isnumber(L, 5) || !lua_isnumber(L, 6)) {
luaL_error(L, "entityCameraSetOrthographic: left/right/top/bottom must be numbers");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t left = (float_t)lua_tonumber(L, 3);
float_t right = (float_t)lua_tonumber(L, 4);
float_t top = (float_t)lua_tonumber(L, 5);
float_t bottom = (float_t)lua_tonumber(L, 6);
entityCameraSetOrthographic(eid, cid, left, right, top, bottom);
return 0;
}
@@ -0,0 +1,62 @@
/**
* 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 entitycamera module within the given script context.
* Provides get/set functions for znear, zfar, and projection type constants.
*
* @param ctx The script context to register the module in.
*/
void moduleEntityCamera(scriptcontext_t *ctx);
/**
* Lua binding: entityCameraGetZNear(entityId, componentId) -> number
*/
int moduleEntityCameraGetZNear(lua_State *L);
/**
* Lua binding: entityCameraSetZNear(entityId, componentId, number)
*/
int moduleEntityCameraSetZNear(lua_State *L);
/**
* Lua binding: entityCameraGetZFar(entityId, componentId) -> number
*/
int moduleEntityCameraGetZFar(lua_State *L);
/**
* Lua binding: entityCameraSetZFar(entityId, componentId, number)
*/
int moduleEntityCameraSetZFar(lua_State *L);
/**
* Lua binding: entityCameraGetProjType(entityId, componentId) -> number
*/
int moduleEntityCameraGetProjType(lua_State *L);
/**
* Lua binding: entityCameraSetProjType(entityId, componentId, number)
*/
int moduleEntityCameraSetProjType(lua_State *L);
/**
* Lua binding: entityCameraGetFov(entityId, componentId) -> number (radians)
*/
int moduleEntityCameraGetFov(lua_State *L);
/**
* Lua binding: entityCameraSetFov(entityId, componentId, number)
*/
int moduleEntityCameraSetFov(lua_State *L);
/**
* Lua binding: entityCameraSetOrthographic(entityId, componentId, left, right, top, bottom)
*/
int moduleEntityCameraSetOrthographic(lua_State *L);
@@ -0,0 +1,209 @@
/**
* 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/component/display/entitymaterial.h"
#include "display/shader/shaderunlit.h"
#include "util/string.h"
void moduleEntityMaterial(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
// shadermaterial_mt: wraps shadermaterial_t* pointer
if(luaL_newmetatable(l, "shadermaterial_mt")) {
lua_pushcfunction(l, moduleEntityMaterialShaderMaterialIndex);
lua_setfield(l, -2, "__index");
}
lua_pop(l, 1);
// shaderunlitmaterial_mt: wraps shaderunlitmaterial_t* pointer
if(luaL_newmetatable(l, "shaderunlitmaterial_mt")) {
lua_pushcfunction(l, moduleEntityMaterialUnlitIndex);
lua_setfield(l, -2, "__index");
lua_pushcfunction(l, moduleEntityMaterialUnlitNewIndex);
lua_setfield(l, -2, "__newindex");
}
lua_pop(l, 1);
lua_register(l, "entityMaterialGetShader", moduleEntityMaterialGetShader);
lua_register(l, "entityMaterialSetShader", moduleEntityMaterialSetShader);
lua_register(l, "entityMaterialGetShaderMaterial", moduleEntityMaterialGetShaderMaterial);
}
int moduleEntityMaterialGetShader(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "Entity ID must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "Component ID must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
shader_t *shader = entityMaterialGetShader(eid, cid);
if(shader == NULL) {
lua_pushnil(L);
} else {
lua_pushlightuserdata(L, shader);
}
return 1;
}
int moduleEntityMaterialSetShader(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "Entity ID must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "Component ID must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
shader_t *shader = NULL;
if(!lua_isnil(L, 3)) {
if(!lua_isuserdata(L, 3)) {
luaL_error(L, "Shader must be a userdata or nil");
return 0;
}
shader = (shader_t *)lua_touserdata(L, 3);
}
entityMaterialSetShader(eid, cid, shader);
return 0;
}
int moduleEntityMaterialGetShaderMaterial(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityMaterialGetShaderMaterial: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityMaterialGetShaderMaterial: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
shadermaterial_t *mat = entityMaterialGetShaderMaterial(eid, cid);
if(mat == NULL) {
lua_pushnil(L);
return 1;
}
shadermaterial_t **pmat = (shadermaterial_t **)lua_newuserdata(
L, sizeof(shadermaterial_t *)
);
*pmat = mat;
luaL_getmetatable(L, "shadermaterial_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityMaterialShaderMaterialIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
shadermaterial_t *mat = *(shadermaterial_t **)luaL_checkudata(
L, 1, "shadermaterial_mt"
);
assertNotNull(mat, "Shader material pointer cannot be NULL");
const char_t *key = lua_tostring(L, 2);
if(key != NULL && stringCompare(key, "unlit") == 0) {
shaderunlitmaterial_t **punlit = (shaderunlitmaterial_t **)lua_newuserdata(
L, sizeof(shaderunlitmaterial_t *)
);
*punlit = &(mat->unlit);
luaL_getmetatable(L, "shaderunlitmaterial_mt");
lua_setmetatable(L, -2);
return 1;
}
lua_pushnil(L);
return 1;
}
int moduleEntityMaterialUnlitIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
shaderunlitmaterial_t *unlit = *(shaderunlitmaterial_t **)luaL_checkudata(
L, 1, "shaderunlitmaterial_mt"
);
assertNotNull(unlit, "Unlit material pointer cannot be NULL");
const char_t *key = lua_tostring(L, 2);
if(key == NULL) {
lua_pushnil(L);
return 1;
}
if(stringCompare(key, "color") == 0) {
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
*color = unlit->color;
luaL_getmetatable(L, "color_mt");
lua_setmetatable(L, -2);
return 1;
}
if(stringCompare(key, "texture") == 0) {
if(unlit->texture == NULL) {
lua_pushnil(L);
} else {
lua_pushlightuserdata(L, unlit->texture);
}
return 1;
}
lua_pushnil(L);
return 1;
}
int moduleEntityMaterialUnlitNewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
shaderunlitmaterial_t *unlit = *(shaderunlitmaterial_t **)luaL_checkudata(
L, 1, "shaderunlitmaterial_mt"
);
assertNotNull(unlit, "Unlit material pointer cannot be NULL");
const char_t *key = lua_tostring(L, 2);
if(key == NULL) return 0;
if(stringCompare(key, "color") == 0) {
color_t *color = (color_t *)luaL_checkudata(L, 3, "color_mt");
unlit->color = *color;
return 0;
}
if(stringCompare(key, "texture") == 0) {
if(lua_isnil(L, 3)) {
unlit->texture = NULL;
} else if(lua_isuserdata(L, 3)) {
unlit->texture = (texture_t *)lua_touserdata(L, 3);
} else {
luaL_error(L, "shaderunlitmaterial texture must be a userdata or nil");
}
return 0;
}
return 0;
}
@@ -0,0 +1,52 @@
/**
* 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 entitymaterial module within the given script context.
* Provides get/set functions for the shader pointer (lightuserdata).
*
* @param ctx The script context to register the module in.
*/
void moduleEntityMaterial(scriptcontext_t *ctx);
/**
* Lua binding: entityMaterialGetShader(entityId, componentId) -> lightuserdata or nil
*/
int moduleEntityMaterialGetShader(lua_State *L);
/**
* Lua binding: entityMaterialSetShader(entityId, componentId, shader_lightuserdata|nil)
*/
int moduleEntityMaterialSetShader(lua_State *L);
/**
* Lua binding: entityMaterialGetShaderMaterial(entityId, componentId)
* Returns a shadermaterial userdata (shadermaterial_mt) holding a pointer
* into the entity's material component, or nil on failure.
*/
int moduleEntityMaterialGetShaderMaterial(lua_State *L);
/**
* __index metamethod for shadermaterial_mt userdata.
* Supports field: "unlit" -> shaderunlitmaterial_mt userdata.
*/
int moduleEntityMaterialShaderMaterialIndex(lua_State *L);
/**
* __index metamethod for shaderunlitmaterial_mt userdata.
* Supports fields: "color" -> color_mt copy, "texture" -> lightuserdata or nil.
*/
int moduleEntityMaterialUnlitIndex(lua_State *L);
/**
* __newindex metamethod for shaderunlitmaterial_mt userdata.
* Supports fields: "color" <- color_mt, "texture" <- lightuserdata or nil.
*/
int moduleEntityMaterialUnlitNewIndex(lua_State *L);
@@ -0,0 +1,70 @@
/**
* 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/component/display/entitymesh.h"
void moduleEntityMesh(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
lua_register(l, "entityMeshGetMesh", moduleEntityMeshGetMesh);
lua_register(l, "entityMeshSetMesh", moduleEntityMeshSetMesh);
}
int moduleEntityMeshGetMesh(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityMeshGetMesh: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityMeshGetMesh: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
mesh_t *mesh = entityMeshGetMesh(eid, cid);
if(mesh == NULL) {
lua_pushnil(L);
} else {
lua_pushlightuserdata(L, mesh);
}
return 1;
}
int moduleEntityMeshSetMesh(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityMeshSetMesh: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityMeshSetMesh: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
mesh_t *mesh = NULL;
if(!lua_isnil(L, 3)) {
if(!lua_isuserdata(L, 3)) {
luaL_error(L, "entityMeshSetMesh: mesh must be a userdata or nil");
return 0;
}
mesh = (mesh_t *)lua_touserdata(L, 3);
}
entityMeshSetMesh(eid, cid, mesh);
return 0;
}
@@ -0,0 +1,27 @@
/**
* 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 entitymesh module within the given script context.
* Provides get/set functions for the mesh pointer (lightuserdata).
*
* @param ctx The script context to register the module in.
*/
void moduleEntityMesh(scriptcontext_t *ctx);
/**
* Lua binding: entityMeshGetMesh(entityId, componentId) -> lightuserdata or nil
*/
int moduleEntityMeshGetMesh(lua_State *L);
/**
* Lua binding: entityMeshSetMesh(entityId, componentId, mesh_lightuserdata|nil)
*/
int moduleEntityMeshSetMesh(lua_State *L);
@@ -0,0 +1,383 @@
/**
* 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 "util/memory.h"
#include "entity/component/physics/entityphysics.h"
void moduleEntityPhysics(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
// Body type constants
lua_pushnumber(l, PHYSICS_BODY_STATIC);
lua_setglobal(l, "PHYSICS_BODY_STATIC");
lua_pushnumber(l, PHYSICS_BODY_DYNAMIC);
lua_setglobal(l, "PHYSICS_BODY_DYNAMIC");
lua_pushnumber(l, PHYSICS_BODY_KINEMATIC);
lua_setglobal(l, "PHYSICS_BODY_KINEMATIC");
// Shape type constants
lua_pushnumber(l, PHYSICS_SHAPE_CUBE);
lua_setglobal(l, "PHYSICS_SHAPE_CUBE");
lua_pushnumber(l, PHYSICS_SHAPE_SPHERE);
lua_setglobal(l, "PHYSICS_SHAPE_SPHERE");
lua_pushnumber(l, PHYSICS_SHAPE_CAPSULE);
lua_setglobal(l, "PHYSICS_SHAPE_CAPSULE");
lua_pushnumber(l, PHYSICS_SHAPE_PLANE);
lua_setglobal(l, "PHYSICS_SHAPE_PLANE");
// Physics shape metatable
if(luaL_newmetatable(l, "physicsshape_mt")) {
lua_pushcfunction(l, moduleEntityPhysicsShapeToString);
lua_setfield(l, -2, "__tostring");
}
lua_pop(l, 1);
// Shape constructors
lua_register(l, "physicsShapeCube", modulePhysicsShapeCube);
lua_register(l, "physicsShapeSphere", modulePhysicsShapeSphere);
lua_register(l, "physicsShapeCapsule", modulePhysicsShapeCapsule);
lua_register(l, "physicsShapePlane", modulePhysicsShapePlane);
// Entity physics functions
lua_register(l, "entityPhysicsGetVelocity", moduleEntityPhysicsGetVelocity);
lua_register(l, "entityPhysicsSetVelocity", moduleEntityPhysicsSetVelocity);
lua_register(l, "entityPhysicsApplyImpulse", moduleEntityPhysicsApplyImpulse);
lua_register(l, "entityPhysicsIsOnGround", moduleEntityPhysicsIsOnGround);
lua_register(l, "entityPhysicsGetShape", moduleEntityPhysicsGetShape);
lua_register(l, "entityPhysicsSetShape", moduleEntityPhysicsSetShape);
lua_register(l, "entityPhysicsGetBodyType", moduleEntityPhysicsGetBodyType);
lua_register(l, "entityPhysicsSetBodyType", moduleEntityPhysicsSetBodyType);
lua_register(l, "entityPhysicsGetGravityScale", moduleEntityPhysicsGetGravityScale);
lua_register(l, "entityPhysicsSetGravityScale", moduleEntityPhysicsSetGravityScale);
}
int moduleEntityPhysicsShapeToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
physicsshape_t *shape = (physicsshape_t *)luaL_checkudata(L, 1, "physicsshape_mt");
assertNotNull(shape, "Physics shape pointer cannot be NULL");
const char_t *typeName = "unknown";
switch(shape->type) {
case PHYSICS_SHAPE_CUBE: typeName = "cube"; break;
case PHYSICS_SHAPE_SPHERE: typeName = "sphere"; break;
case PHYSICS_SHAPE_CAPSULE: typeName = "capsule"; break;
case PHYSICS_SHAPE_PLANE: typeName = "plane"; break;
}
lua_pushfstring(L, "physicsshape(%s)", typeName);
return 1;
}
int moduleEntityPhysicsGetVelocity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsGetVelocity: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsGetVelocity: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)lua_newuserdata(L, sizeof(vec3));
memoryZero(v, sizeof(vec3));
entityPhysicsGetVelocity(eid, cid, *v);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPhysicsSetVelocity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsSetVelocity: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsSetVelocity: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPhysicsSetVelocity(eid, cid, *v);
return 0;
}
int moduleEntityPhysicsApplyImpulse(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsApplyImpulse: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsApplyImpulse: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPhysicsApplyImpulse(eid, cid, *v);
return 0;
}
int moduleEntityPhysicsIsOnGround(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsIsOnGround: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsIsOnGround: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushboolean(L, entityPhysicsIsOnGround(eid, cid) ? 1 : 0);
return 1;
}
int moduleEntityPhysicsSetShape(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsSetShape: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsSetShape: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
physicsshape_t *shape = (physicsshape_t *)luaL_checkudata(L, 3, "physicsshape_mt");
entityPhysicsSetShape(eid, cid, *shape);
return 0;
}
int moduleEntityPhysicsGetShape(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsGetShape: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsGetShape: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
*shape = entityPhysicsGetShape(eid, cid);
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPhysicsGetBodyType(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsGetBodyType: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsGetBodyType: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityPhysicsGetBodyType(eid, cid));
return 1;
}
int moduleEntityPhysicsSetBodyType(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsSetBodyType: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsSetBodyType: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityPhysicsSetBodyType: bodyType must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
physicsbodytype_t type = (physicsbodytype_t)lua_tonumber(L, 3);
entityPhysicsSetBodyType(eid, cid, type);
return 0;
}
int moduleEntityPhysicsGetGravityScale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsGetGravityScale: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsGetGravityScale: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityPhysicsGetGravityScale(eid, cid));
return 1;
}
int moduleEntityPhysicsSetGravityScale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsSetGravityScale: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsSetGravityScale: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityPhysicsSetGravityScale: scale must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t scale = (float_t)lua_tonumber(L, 3);
entityPhysicsSetGravityScale(eid, cid, scale);
return 0;
}
int modulePhysicsShapeCube(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
float_t hx = lua_isnumber(L, 1) ? (float_t)lua_tonumber(L, 1) : 0.5f;
float_t hy = lua_isnumber(L, 2) ? (float_t)lua_tonumber(L, 2) : 0.5f;
float_t hz = lua_isnumber(L, 3) ? (float_t)lua_tonumber(L, 3) : 0.5f;
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
memoryZero(shape, sizeof(physicsshape_t));
shape->type = PHYSICS_SHAPE_CUBE;
shape->data.cube.halfExtents[0] = hx;
shape->data.cube.halfExtents[1] = hy;
shape->data.cube.halfExtents[2] = hz;
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
int modulePhysicsShapeSphere(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "physicsShapeSphere: radius must be a number");
return 0;
}
float_t radius = (float_t)lua_tonumber(L, 1);
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
memoryZero(shape, sizeof(physicsshape_t));
shape->type = PHYSICS_SHAPE_SPHERE;
shape->data.sphere.radius = radius;
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
int modulePhysicsShapeCapsule(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "physicsShapeCapsule: radius must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "physicsShapeCapsule: halfHeight must be a number");
return 0;
}
float_t radius = (float_t)lua_tonumber(L, 1);
float_t halfHeight = (float_t)lua_tonumber(L, 2);
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
memoryZero(shape, sizeof(physicsshape_t));
shape->type = PHYSICS_SHAPE_CAPSULE;
shape->data.capsule.radius = radius;
shape->data.capsule.halfHeight = halfHeight;
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
int modulePhysicsShapePlane(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "physicsShapePlane: normalX must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "physicsShapePlane: normalY must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "physicsShapePlane: normalZ must be a number");
return 0;
}
if(!lua_isnumber(L, 4)) {
luaL_error(L, "physicsShapePlane: distance must be a number");
return 0;
}
float_t nx = (float_t)lua_tonumber(L, 1);
float_t ny = (float_t)lua_tonumber(L, 2);
float_t nz = (float_t)lua_tonumber(L, 3);
float_t distance = (float_t)lua_tonumber(L, 4);
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
memoryZero(shape, sizeof(physicsshape_t));
shape->type = PHYSICS_SHAPE_PLANE;
shape->data.plane.normal[0] = nx;
shape->data.plane.normal[1] = ny;
shape->data.plane.normal[2] = nz;
shape->data.plane.distance = distance;
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
@@ -0,0 +1,64 @@
/**
* 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 entityphysics module within the given script context.
* Provides velocity, impulse, onGround, and shape functions, plus body type
* and shape type constants. Shape constructors: physicsShapeCube,
* physicsShapeSphere, physicsShapeCapsule, physicsShapePlane.
*
* @param ctx The script context to register the module in.
*/
void moduleEntityPhysics(scriptcontext_t *ctx);
/** Lua binding: entityPhysicsGetVelocity(entityId, componentId) -> vec3 */
int moduleEntityPhysicsGetVelocity(lua_State *L);
/** Lua binding: entityPhysicsSetVelocity(entityId, componentId, vec3) */
int moduleEntityPhysicsSetVelocity(lua_State *L);
/** Lua binding: entityPhysicsApplyImpulse(entityId, componentId, vec3) */
int moduleEntityPhysicsApplyImpulse(lua_State *L);
/** Lua binding: entityPhysicsIsOnGround(entityId, componentId) -> boolean */
int moduleEntityPhysicsIsOnGround(lua_State *L);
/** Lua binding: entityPhysicsSetShape(entityId, componentId, physicsshape) */
int moduleEntityPhysicsSetShape(lua_State *L);
/** Lua binding: physicsShapeCube(halfX, halfY, halfZ) -> physicsshape */
int modulePhysicsShapeCube(lua_State *L);
/** Lua binding: physicsShapeSphere(radius) -> physicsshape */
int modulePhysicsShapeSphere(lua_State *L);
/** Lua binding: physicsShapeCapsule(radius, halfHeight) -> physicsshape */
int modulePhysicsShapeCapsule(lua_State *L);
/** Lua binding: physicsShapePlane(normalX, normalY, normalZ, distance) -> physicsshape */
int modulePhysicsShapePlane(lua_State *L);
/** Lua binding: entityPhysicsGetShape(entityId, componentId) -> physicsshape */
int moduleEntityPhysicsGetShape(lua_State *L);
/** Lua binding: entityPhysicsGetBodyType(entityId, componentId) -> number */
int moduleEntityPhysicsGetBodyType(lua_State *L);
/** Lua binding: entityPhysicsSetBodyType(entityId, componentId, number) */
int moduleEntityPhysicsSetBodyType(lua_State *L);
/** Lua binding: entityPhysicsGetGravityScale(entityId, componentId) -> number */
int moduleEntityPhysicsGetGravityScale(lua_State *L);
/** Lua binding: entityPhysicsSetGravityScale(entityId, componentId, number) */
int moduleEntityPhysicsSetGravityScale(lua_State *L);
/** Metatable __tostring for physicsshape userdata */
int moduleEntityPhysicsShapeToString(lua_State *L);
@@ -0,0 +1,172 @@
/**
* 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 "util/memory.h"
#include "entity/component/display/entityposition.h"
void moduleEntityPosition(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
lua_register(l, "entityPositionGetPosition", moduleEntityPositionGetPosition);
lua_register(l, "entityPositionSetPosition", moduleEntityPositionSetPosition);
lua_register(l, "entityPositionGetRotation", moduleEntityPositionGetRotation);
lua_register(l, "entityPositionSetRotation", moduleEntityPositionSetRotation);
lua_register(l, "entityPositionGetScale", moduleEntityPositionGetScale);
lua_register(l, "entityPositionSetScale", moduleEntityPositionSetScale);
lua_register(l, "entityPositionLookAt", moduleEntityPositionLookAt);
}
int moduleEntityPositionGetPosition(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionGetPosition: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionGetPosition: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)lua_newuserdata(L, sizeof(vec3));
memoryZero(v, sizeof(vec3));
entityPositionGetPosition(eid, cid, *v);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPositionSetPosition(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionSetPosition: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionSetPosition: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPositionSetPosition(eid, cid, *v);
return 0;
}
int moduleEntityPositionGetRotation(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionGetRotation: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionGetRotation: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)lua_newuserdata(L, sizeof(vec3));
memoryZero(v, sizeof(vec3));
entityPositionGetRotation(eid, cid, *v);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPositionSetRotation(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionSetRotation: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionSetRotation: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPositionSetRotation(eid, cid, *v);
return 0;
}
int moduleEntityPositionGetScale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionGetScale: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionGetScale: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)lua_newuserdata(L, sizeof(vec3));
memoryZero(v, sizeof(vec3));
entityPositionGetScale(eid, cid, *v);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPositionSetScale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionSetScale: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionSetScale: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPositionSetScale(eid, cid, *v);
return 0;
}
int moduleEntityPositionLookAt(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionLookAt: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionLookAt: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *target = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
vec3 *up = (vec3 *)luaL_checkudata(L, 4, "vec3_mt");
vec3 *eye = (vec3 *)luaL_checkudata(L, 5, "vec3_mt");
entityPositionLookAt(eid, cid, *target, *up, *eye);
return 0;
}
@@ -0,0 +1,53 @@
/**
* 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 entityposition module within the given script context.
* Provides get/set functions for position, rotation, scale, and lookAt.
*
* @param ctx The script context to register the module in.
*/
void moduleEntityPosition(scriptcontext_t *ctx);
/**
* Lua binding: entityPositionGetPosition(entityId, componentId) -> vec3
*/
int moduleEntityPositionGetPosition(lua_State *L);
/**
* Lua binding: entityPositionSetPosition(entityId, componentId, vec3)
*/
int moduleEntityPositionSetPosition(lua_State *L);
/**
* Lua binding: entityPositionGetRotation(entityId, componentId) -> vec3 (radians)
*/
int moduleEntityPositionGetRotation(lua_State *L);
/**
* Lua binding: entityPositionSetRotation(entityId, componentId, vec3) (radians)
*/
int moduleEntityPositionSetRotation(lua_State *L);
/**
* Lua binding: entityPositionGetScale(entityId, componentId) -> vec3
*/
int moduleEntityPositionGetScale(lua_State *L);
/**
* Lua binding: entityPositionSetScale(entityId, componentId, vec3)
*/
int moduleEntityPositionSetScale(lua_State *L);
/**
* Lua binding: entityPositionLookAt(entityId, componentId, target, up, eye)
* All vec3 arguments.
*/
int moduleEntityPositionLookAt(lua_State *L);
@@ -0,0 +1,77 @@
/**
* 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 "error/error.h"
#include "entity/component/script/entityscript.h"
void moduleEntityScript(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
lua_register(l, "entityScriptExec", moduleEntityScriptExec);
lua_register(l, "entityScriptExecAsset", moduleEntityScriptExecAsset);
}
int moduleEntityScriptExec(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityScriptExec: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityScriptExec: componentId must be a number");
return 0;
}
if(!lua_isstring(L, 3)) {
luaL_error(L, "entityScriptExec: script must be a string");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
const char_t *script = lua_tostring(L, 3);
errorret_t ret = entityScriptExec(eid, cid, script);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(L, "entityScriptExec failed");
return 0;
}
return 0;
}
int moduleEntityScriptExecAsset(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityScriptExecAsset: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityScriptExecAsset: componentId must be a number");
return 0;
}
if(!lua_isstring(L, 3)) {
luaL_error(L, "entityScriptExecAsset: assetName must be a string");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
const char_t *assetName = lua_tostring(L, 3);
errorret_t ret = entityScriptExecAsset(eid, cid, assetName);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(L, "entityScriptExecAsset failed");
return 0;
}
return 0;
}
@@ -0,0 +1,29 @@
/**
* 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 entityscript module within the given script context.
* Provides functions to execute Lua code on another entity's script component.
*
* @param ctx The script context to register the module in.
*/
void moduleEntityScript(scriptcontext_t *ctx);
/**
* Lua binding: entityScriptExec(entityId, componentId, scriptString)
* Executes an inline Lua string on the target entity's script component.
*/
int moduleEntityScriptExec(lua_State *L);
/**
* Lua binding: entityScriptExecAsset(entityId, componentId, assetName)
* Executes a Lua script file from assets on the target entity's script component.
*/
int moduleEntityScriptExecAsset(lua_State *L);
+14
View File
@@ -22,6 +22,13 @@
#include "script/module/display/modulescreen.h" #include "script/module/display/modulescreen.h"
#include "script/module/display/moduletexture.h" #include "script/module/display/moduletexture.h"
#include "script/module/display/moduletileset.h" #include "script/module/display/moduletileset.h"
#include "script/module/entity/moduleentity.h"
#include "script/module/entity/moduleentityposition.h"
#include "script/module/entity/moduleentitycamera.h"
#include "script/module/entity/moduleentitymesh.h"
#include "script/module/entity/moduleentitymaterial.h"
#include "script/module/entity/moduleentityphysics.h"
#include "script/module/entity/moduleentityscript.h"
#include "script/scriptgame.h" #include "script/scriptgame.h"
#include "util/string.h" #include "util/string.h"
@@ -42,6 +49,13 @@ const scriptmodule_t SCRIPT_MODULE_LIST[] = {
{ .name = "texture", .callback = moduleTexture }, { .name = "texture", .callback = moduleTexture },
{ .name = "tileset", .callback = moduleTileset }, { .name = "tileset", .callback = moduleTileset },
{ .name = "shader", .callback = moduleShader }, { .name = "shader", .callback = moduleShader },
{ .name = "entity", .callback = moduleEntity },
{ .name = "entityposition", .callback = moduleEntityPosition },
{ .name = "entitycamera", .callback = moduleEntityCamera },
{ .name = "entitymesh", .callback = moduleEntityMesh },
{ .name = "entitymaterial", .callback = moduleEntityMaterial },
{ .name = "entityphysics", .callback = moduleEntityPhysics },
{ .name = "entityscript", .callback = moduleEntityScript },
#ifdef SCRIPT_GAME_LIST #ifdef SCRIPT_GAME_LIST
SCRIPT_GAME_LIST SCRIPT_GAME_LIST