Add entity scripting
This commit is contained in:
@@ -5,3 +5,4 @@
|
|||||||
|
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
add_subdirectory(physics)
|
add_subdirectory(physics)
|
||||||
|
add_subdirectory(script)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# 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
|
||||||
|
entityscript.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
);
|
||||||
@@ -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(ENTITYSCRIPT, entityscript_t, entityscript, entityScriptInit, entityScriptDispose)
|
||||||
@@ -12,3 +12,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
# Subdirectories
|
# Subdirectories
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
add_subdirectory(physics)
|
add_subdirectory(physics)
|
||||||
|
add_subdirectory(script)
|
||||||
|
|||||||
@@ -9,12 +9,25 @@
|
|||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "entity/entitymanager.h"
|
#include "entity/entitymanager.h"
|
||||||
#include "entity/entity.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) {
|
void moduleEntity(scriptcontext_t *ctx) {
|
||||||
assertNotNull(ctx, "Script context cannot be NULL");
|
assertNotNull(ctx, "Script context cannot be NULL");
|
||||||
|
|
||||||
lua_register(ctx->luaState, "entityAdd", moduleEntityAdd);
|
lua_register(ctx->luaState, "entityAdd", moduleEntityAdd);
|
||||||
lua_register(ctx->luaState, "entityRemove", moduleEntityRemove);
|
lua_register(ctx->luaState, "entityRemove", moduleEntityRemove);
|
||||||
|
|
||||||
|
moduleEntityPosition(ctx);
|
||||||
|
moduleEntityCamera(ctx);
|
||||||
|
moduleEntityMesh(ctx);
|
||||||
|
moduleEntityMaterial(ctx);
|
||||||
|
moduleEntityPhysics(ctx);
|
||||||
|
moduleEntityScript(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int moduleEntityAdd(lua_State *L) {
|
int moduleEntityAdd(lua_State *L) {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# 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
|
||||||
|
)
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "script/module/entity/display/moduleentitymesh.h"
|
#include "script/module/entity/display/moduleentitymesh.h"
|
||||||
#include "script/module/entity/display/moduleentitymaterial.h"
|
#include "script/module/entity/display/moduleentitymaterial.h"
|
||||||
#include "script/module/entity/physics/moduleentityphysics.h"
|
#include "script/module/entity/physics/moduleentityphysics.h"
|
||||||
|
#include "script/module/entity/script/moduleentityscript.h"
|
||||||
|
|
||||||
#include "script/scriptgame.h"
|
#include "script/scriptgame.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
@@ -40,6 +41,7 @@ const scriptmodule_t SCRIPT_MODULE_LIST[] = {
|
|||||||
REG("entitymesh", moduleEntityMesh)
|
REG("entitymesh", moduleEntityMesh)
|
||||||
REG("entitymaterial", moduleEntityMaterial)
|
REG("entitymaterial", moduleEntityMaterial)
|
||||||
REG("entityphysics", moduleEntityPhysics)
|
REG("entityphysics", moduleEntityPhysics)
|
||||||
|
REG("entityscript", moduleEntityScript)
|
||||||
REG("entity", moduleEntity)
|
REG("entity", moduleEntity)
|
||||||
REG("input", moduleInput)
|
REG("input", moduleInput)
|
||||||
REG("platform", modulePlatform)
|
REG("platform", modulePlatform)
|
||||||
|
|||||||
Reference in New Issue
Block a user