Refactored and simplified lua stuff a lot.
Some checks failed
Build Dusk / run-tests (push) Failing after 1m23s
Build Dusk / build-linux (push) Failing after 1m47s
Build Dusk / build-psp (push) Failing after 1m41s

This commit is contained in:
2026-02-01 21:28:21 -06:00
parent 78e1ae885a
commit 053778a502
53 changed files with 638 additions and 804 deletions

View File

@@ -45,5 +45,5 @@ int moduleMapLoad(lua_State *L) {
void moduleMapSystem(scriptcontext_t *context) { void moduleMapSystem(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
scriptContextRegFunc(context, "mapLoad", moduleMapLoad); lua_register(context->luaState, "mapLoad", moduleMapLoad);
} }

View File

@@ -128,8 +128,8 @@ int scriptFuncEntitySetZ(lua_State *L) {
void scriptFuncEntity(scriptcontext_t *context) { void scriptFuncEntity(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
scriptContextRegFunc(context, "entityAdd", scriptFuncEntityAdd); lua_register(context->luaState, "entityAdd", scriptFuncEntityAdd);
scriptContextRegFunc(context, "entitySetX", scriptFuncEntitySetX); lua_register(context->luaState, "entitySetX", scriptFuncEntitySetX);
scriptContextRegFunc(context, "entitySetY", scriptFuncEntitySetY); lua_register(context->luaState, "entitySetY", scriptFuncEntitySetY);
scriptContextRegFunc(context, "entitySetZ", scriptFuncEntitySetZ); lua_register(context->luaState, "entitySetZ", scriptFuncEntitySetZ);
} }

View File

@@ -1,10 +1,17 @@
# Copyright (c) 2026 Dominic Masters # Copyright (c) 2025 Dominic Masters
# #
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
# Sources # Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
scriptstruct.c ui.c
) uitext.c
uidebug.c
uiframe.c
uitextbox.c
)
# Subdirs
add_subdirectory(element)

67
archive/ui/ui.c Normal file
View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "ui.h"
#include "assert/assert.h"
#include "ui/uidebug.h"
#include "util/memory.h"
#include "display/tileset/tileset_minogram.h"
#include "display/screen.h"
// #include "ui/uitextbox.h"
ui_t UI;
errorret_t uiInit(void) {
memoryZero(&UI, sizeof(ui_t));
cameraInitOrthographic(&UI.camera);
// Initialize UI components here
uiSetFont(&TILESET_MINOGRAM);
errorOk();
}
void uiUpdate(void) {
// Update UI state here
UI.camera.orthographic.left = 0;
UI.camera.orthographic.right = SCREEN.width;
UI.camera.orthographic.top = 0;
UI.camera.orthographic.bottom = SCREEN.height;
// uiTextboxUpdate();
}
void uiRender(void) {
cameraPushMatrix(&UI.camera);
// Render UI elements here
if(UI.fontTexture.width > 0) {
uiDebugRender(UI.fontTileset, &UI.fontTexture);
}
cameraPopMatrix();
}
errorret_t uiSetFont(const tileset_t *fontTileset) {
if(UI.fontTexture.width > 0) {
textureDispose(&UI.fontTexture);
UI.fontTexture.width = -1;
}
assertNotNull(fontTileset, "Font tileset cannot be NULL.");
UI.fontTileset = fontTileset;
errorChain(assetLoad(UI.fontTileset->image, &UI.fontTexture));
errorOk();
}
void uiDispose(void) {
if(UI.fontTexture.width > 0) {
textureDispose(&UI.fontTexture);
UI.fontTexture.width = -1;
}
}

51
archive/ui/ui.h Normal file
View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "asset/asset.h"
#include "display/texture.h"
#include "display/tileset/tileset.h"
#include "display/camera/camera.h"
typedef struct {
camera_t camera;
texture_t fontTexture;
const tileset_t *fontTileset;
} ui_t;
extern ui_t UI;
/**
* Initializes the UI system, loading necessary resources.
*
* @return An errorret_t indicating success or failure.
*/
errorret_t uiInit(void);
/**
* Updates the UI state, handling user interactions and animations.
*/
void uiUpdate(void);
/**
* Renders the UI elements to the screen.
*/
void uiRender(void);
/**
* Sets the font tileset for UI text rendering.
*
* @param fontTileset Pointer to the tileset to use for UI fonts.
*
* @return An errorret_t indicating success or failure.
*/
errorret_t uiSetFont(const tileset_t *fontTileset);
/**
* Cleans up and frees all UI resources.
*/
void uiDispose(void);

View File

@@ -1,21 +1,17 @@
module('spritebatch') module('spritebatch')
module('time') -- module('time')
module('camera') module('camera')
module('glm') -- module('glm')
module('color') module('color')
camera = cameraCreate(CAMERA_PROJECTION_TYPE_PERSPECTIVE) camera = cameraCreate(CAMERA_PROJECTION_TYPE_PERSPECTIVE)
color = colorBlue()
function sceneDispose() function sceneDispose()
-- print('Disposing initial scene') -- print('Disposing initial scene')
end end
function sceneUpdate() function sceneUpdate()
color.r = 255 * (math.sin(TIME.time) + 1) * 0.5
color.g = 255 * (math.sin(TIME.time + 2) + 1) * 0.5
color.b = 255 * (math.sin(TIME.time + 4) + 1) * 0.5
end end
function sceneRender() function sceneRender()
@@ -25,7 +21,7 @@ function sceneRender()
nil, nil,
0, 0, 0, 0,
1, 2, 1, 2,
color colorBlue()
) )
spriteBatchFlush() spriteBatchFlush()

View File

@@ -3,4 +3,5 @@
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_asset(TILESET minogram.png type=PALETTIZED tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering add_asset(TILESET minogram.png type=PALETTIZED tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering
add_asset(SCRIPT test.lua)

View File

@@ -1,6 +1,9 @@
module('ui') module('spritebatch')
module('color')
function draw(x, y) function render(x, y, w, h)
uiDrawRect(COLOR_WHITE, x, y, 32, 32) spriteBatchPush(
nil,
x, y,
w, h
)
end end

View File

@@ -65,5 +65,4 @@ add_subdirectory(script)
add_subdirectory(thread) add_subdirectory(thread)
add_subdirectory(time) add_subdirectory(time)
add_subdirectory(ui) add_subdirectory(ui)
add_subdirectory(ui2)
add_subdirectory(util) add_subdirectory(util)

View File

@@ -23,8 +23,6 @@ typedef enum {
} cameraviewtype_t; } cameraviewtype_t;
typedef struct { typedef struct {
cameraprojectiontype_t projType;
cameraviewtype_t viewType;
union { union {
mat4 view; mat4 view;
@@ -63,6 +61,9 @@ typedef struct {
float_t nearClip; float_t nearClip;
float_t farClip; float_t farClip;
cameraprojectiontype_t projType;
cameraviewtype_t viewType;
} camera_t; } camera_t;
/** /**

View File

@@ -8,7 +8,6 @@
#include "event.h" #include "event.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include "script/struct/scriptstruct.h"
void eventInit( void eventInit(
event_t *event, event_t *event,
@@ -228,10 +227,11 @@ void eventInvoke(
lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef); lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
if(eventParams != NULL && metatableName != NULL) { if(eventParams != NULL && metatableName != NULL) {
scriptStructPushMetatable( lua_getmetatable(L, -1);
listener->user.script.context, luaL_getmetatable(L, metatableName);
metatableName, assertTrue(
(void *)eventParams lua_rawequal(L, -1, -2),
"Event parameter metatable does not match expected type"
); );
} }

View File

@@ -12,5 +12,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
) )
# Subdirectories # Subdirectories
add_subdirectory(module) add_subdirectory(module)
add_subdirectory(struct)

View File

@@ -9,19 +9,20 @@
#include "assert/assert.h" #include "assert/assert.h"
#include "display/camera/camera.h" #include "display/camera/camera.h"
#include "util/memory.h" #include "util/memory.h"
#include "script/struct/scriptstruct.h"
#include "util/string.h" #include "util/string.h"
void moduleCamera(scriptcontext_t *context) { void moduleCamera(scriptcontext_t *context) {
assertNotNull(context, "Context cannot be NULL."); assertNotNull(context, "Context cannot be NULL.");
// Structure // Create metatable for camera structure.
scriptStructRegister( if(luaL_newmetatable(context->luaState, "camera_mt")) {
context, // Metatable methods
"camera_mt", lua_pushcfunction(context->luaState, moduleCameraIndex);
moduleCameraGetter, lua_setfield(context->luaState, -2, "__index");
moduleCameraSetter lua_pushcfunction(context->luaState, moduleCameraNewIndex);
); lua_setfield(context->luaState, -2, "__newindex");
lua_pop(context->luaState, 1);
}
// Definitions // Definitions
#define MODULE_CAMERA_SCRIPT_LEN 64 #define MODULE_CAMERA_SCRIPT_LEN 64
@@ -67,9 +68,9 @@ void moduleCamera(scriptcontext_t *context) {
); );
// Methods // Methods
scriptContextRegFunc(context, "cameraCreate", moduleCameraCreate); lua_register(context->luaState, "cameraCreate", moduleCameraCreate);
scriptContextRegFunc(context, "cameraPushMatrix", moduleCameraPushMatrix); lua_register(context->luaState, "cameraPushMatrix", moduleCameraPushMatrix);
scriptContextRegFunc(context, "cameraPopMatrix", moduleCameraPopMatrix); lua_register(context->luaState, "cameraPopMatrix", moduleCameraPopMatrix);
} }
int moduleCameraCreate(lua_State *L) { int moduleCameraCreate(lua_State *L) {
@@ -84,12 +85,13 @@ int moduleCameraCreate(lua_State *L) {
projType = (cameraprojectiontype_t)luaL_checkinteger(L, 1); projType = (cameraprojectiontype_t)luaL_checkinteger(L, 1);
} }
// Create camera. // Create camera that Lua will own
camera_t *cam = (camera_t *)lua_newuserdata(L, sizeof(camera_t)); camera_t *cam = (camera_t *)lua_newuserdata(L, sizeof(camera_t));
memoryZero(cam, sizeof(camera_t)); memoryZero(cam, sizeof(camera_t));
// Push metatable. // Set metatable
scriptStructPushMetatable(context, "camera_mt", cam); luaL_getmetatable(L, "camera_mt");
lua_setmetatable(L, -2);
// Init camera // Init camera
switch(projType) { switch(projType) {
@@ -114,9 +116,11 @@ int moduleCameraCreate(lua_State *L) {
int moduleCameraPushMatrix(lua_State *L) { int moduleCameraPushMatrix(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL."); assertNotNull(L, "Lua state cannot be NULL.");
assertTrue(lua_gettop(L) >= 1, "cameraPushMatrix requires 1 arg.");
assertTrue(lua_isuserdata(L, 1), "cameraPushMatrix arg must be userdata.");
// Camera should be provided (pointer to camera_t). // Camera should be provided (pointer to camera_t).
camera_t *cam = *(camera_t **)lua_touserdata(L, 1); camera_t *cam = (camera_t *)luaL_checkudata(L, 1, "camera_mt");
assertNotNull(cam, "Camera pointer cannot be NULL."); assertNotNull(cam, "Camera pointer cannot be NULL.");
cameraPushMatrix(cam); cameraPushMatrix(cam);
@@ -129,64 +133,51 @@ int moduleCameraPopMatrix(lua_State *L) {
return 0; return 0;
} }
void moduleCameraGetter( int moduleCameraIndex(lua_State *l) {
const scriptcontext_t *context, assertNotNull(l, "Lua state cannot be NULL.");
const char_t *key,
const void *structPtr,
scriptvalue_t *outValue
) {
assertNotNull(context, "Script context cannot be NULL.");
assertNotNull(key, "Key cannot be NULL.");
assertNotNull(structPtr, "Structure pointer cannot be NULL.");
assertNotNull(outValue, "Output value cannot be NULL.");
camera_t *cam = (camera_t *)structPtr; const char_t *key = luaL_checkstring(l, 2);
assertStrLenMin(key, 1, "Key cannot be empty.");
camera_t *cam = (camera_t *)luaL_checkudata(l, 1, "camera_mt");
assertNotNull(cam, "Camera pointer cannot be NULL.");
if(stringCompare(key, "near") == 0) { if(stringCompare(key, "near") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->nearClip);
outValue->value.floatValue = cam->nearClip; return 1;
return;
} else if(stringCompare(key, "far") == 0) { } else if(stringCompare(key, "far") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->farClip);
outValue->value.floatValue = cam->farClip; return 1;
return;
} else if(stringCompare(key, "nearClip") == 0) { } else if(stringCompare(key, "nearClip") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->nearClip);
outValue->value.floatValue = cam->nearClip; return 1;
return;
} else if(stringCompare(key, "farClip") == 0) { } else if(stringCompare(key, "farClip") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->farClip);
outValue->value.floatValue = cam->farClip; return 1;
return;
} }
// Perspective relative values // Perspective relative values
if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) { if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
if(stringCompare(key, "left") == 0) { if(stringCompare(key, "left") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->orthographic.left);
outValue->value.floatValue = cam->orthographic.left; return 1;
return;
} else if(stringCompare(key, "right") == 0) { } else if(stringCompare(key, "right") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->orthographic.right);
outValue->value.floatValue = cam->orthographic.right; return 1;
return;
} else if(stringCompare(key, "top") == 0) { } else if(stringCompare(key, "top") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->orthographic.top);
outValue->value.floatValue = cam->orthographic.top; return 1;
return;
} else if(stringCompare(key, "bottom") == 0) { } else if(stringCompare(key, "bottom") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->orthographic.bottom);
outValue->value.floatValue = cam->orthographic.bottom; return 1;
return;
} }
} else if( } else if(
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE || cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
) { ) {
if(stringCompare(key, "fov") == 0) { if(stringCompare(key, "fov") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(l, cam->perspective.fov);
outValue->value.floatValue = cam->perspective.fov; return 1;
return;
} }
} }
@@ -194,107 +185,92 @@ void moduleCameraGetter(
if(cam->viewType == CAMERA_VIEW_TYPE_MATRIX) { if(cam->viewType == CAMERA_VIEW_TYPE_MATRIX) {
} else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) { } else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) {
if(stringCompare(key, "position") == 0) { // TODO: Push vec3
scriptStructPushMetatable(
context,
"vec3_mt",
(void*)&cam->lookat.position
);
outValue->type = SCRIPT_VALUE_TYPE_USERDATA;
return;
}
} else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT) { } else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT) {
} else if(cam->viewType == CAMERA_VIEW_TYPE_2D) { } else if(cam->viewType == CAMERA_VIEW_TYPE_2D) {
} }
lua_pushnil(l);
return 1;
} }
void moduleCameraSetter( int moduleCameraNewIndex(lua_State *l) {
const scriptcontext_t *context, assertNotNull(l, "Lua state cannot be NULL.");
const char_t *key,
void *structPtr, const char_t *key = luaL_checkstring(l, 2);
const scriptvalue_t *inValue assertStrLenMin(key, 1, "Key cannot be empty.");
) {
assertNotNull(context, "Script context cannot be NULL.");
assertNotNull(key, "Key cannot be NULL.");
assertNotNull(structPtr, "Structure pointer cannot be NULL.");
assertNotNull(inValue, "Input value cannot be NULL.");
camera_t *cam = (camera_t *)structPtr; camera_t *cam = (camera_t *)luaL_checkudata(l, 1, "camera_mt");
assertNotNull(cam, "Camera pointer cannot be NULL.");
if(stringCompare(key, "near") == 0) { if(stringCompare(key, "near") == 0 || stringCompare(key, "nearClip") == 0) {
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { if(!lua_isnumber(l, 3)) {
cam->nearClip = inValue->value.floatValue; luaL_error(l, "Camera near clip must be a number.");
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) {
cam->nearClip = (float_t)inValue->value.intValue;
} }
return; cam->nearClip = (float_t)lua_tonumber(l, 3);
} else if(stringCompare(key, "far") == 0) { return 0;
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { }
cam->farClip = inValue->value.floatValue;
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { if(stringCompare(key, "far") == 0 || stringCompare(key, "farClip") == 0) {
cam->farClip = (float_t)inValue->value.intValue; if(!lua_isnumber(l, 3)) {
luaL_error(l, "Camera far clip must be a number.");
} }
return; cam->farClip = (float_t)lua_tonumber(l, 3);
} else if(stringCompare(key, "nearClip") == 0) { return 0;
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) {
cam->nearClip = inValue->value.floatValue;
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) {
cam->nearClip = (float_t)inValue->value.intValue;
}
return;
} else if(stringCompare(key, "farClip") == 0) {
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) {
cam->farClip = inValue->value.floatValue;
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) {
cam->farClip = (float_t)inValue->value.intValue;
}
return;
} }
// Perspective relative values // Perspective relative values
if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) { if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
if(stringCompare(key, "left") == 0) { if(stringCompare(key, "left") == 0) {
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { if(!lua_isnumber(l, 3)) {
cam->orthographic.left = inValue->value.floatValue; luaL_error(l, "Camera orthographic left must be a number.");
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) {
cam->orthographic.left = (float_t)inValue->value.intValue;
} }
return; cam->orthographic.left = (float_t)lua_tonumber(l, 3);
} else if(stringCompare(key, "right") == 0) { return 0;
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { }
cam->orthographic.right = inValue->value.floatValue;
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { if(stringCompare(key, "right") == 0) {
cam->orthographic.right = (float_t)inValue->value.intValue; if(!lua_isnumber(l, 3)) {
luaL_error(l, "Camera orthographic right must be a number.");
} }
return; cam->orthographic.right = (float_t)lua_tonumber(l, 3);
} else if(stringCompare(key, "top") == 0) { return 0;
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { }
cam->orthographic.top = inValue->value.floatValue;
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { if(stringCompare(key, "top") == 0) {
cam->orthographic.top = (float_t)inValue->value.intValue; if(!lua_isnumber(l, 3)) {
luaL_error(l, "Camera orthographic top must be a number.");
} }
return; cam->orthographic.top = (float_t)lua_tonumber(l, 3);
} else if(stringCompare(key, "bottom") == 0) { return 0;
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { }
cam->orthographic.bottom = inValue->value.floatValue;
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { if(stringCompare(key, "bottom") == 0) {
cam->orthographic.bottom = (float_t)inValue->value.intValue; if(!lua_isnumber(l, 3)) {
luaL_error(l, "Camera orthographic bottom must be a number.");
} }
return; cam->orthographic.bottom = (float_t)lua_tonumber(l, 3);
return 0;
} }
} else if( } else if(
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE || cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
) { ) {
if(stringCompare(key, "fov") == 0) { if(stringCompare(key, "fov") == 0) {
if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { if(!lua_isnumber(l, 3)) {
cam->perspective.fov = inValue->value.floatValue; luaL_error(l, "Camera perspective FOV must be a number.");
} else if(inValue->type == SCRIPT_VALUE_TYPE_INT) {
cam->perspective.fov = (float_t)inValue->value.intValue;
} }
return; cam->perspective.fov = (float_t)lua_tonumber(l, 3);
return 0;
} }
} }
luaL_error(
l,
"Attempted to set unknown or read-only camera field '%s'.",
key
);
return 0;
} }

View File

@@ -42,29 +42,13 @@ int moduleCameraPopMatrix(lua_State *L);
/** /**
* Getter for camera structure fields. * Getter for camera structure fields.
* *
* @param context The script context. * @param l The Lua state.
* @param key The field key.
* @param structPtr Pointer to the camera structure.
* @param outValue Output script value.
*/ */
void moduleCameraGetter( int moduleCameraIndex(lua_State *l);
const scriptcontext_t *context,
const char_t *key,
const void *structPtr,
scriptvalue_t *outValue
);
/** /**
* Setter for camera structure fields. * Setter for camera structure fields.
* *
* @param context The script context. * @param l The Lua state.
* @param key The field key.
* @param structPtr Pointer to the camera structure.
* @param inValue Input script value.
*/ */
void moduleCameraSetter( int moduleCameraNewIndex(lua_State *l);
const scriptcontext_t *context,
const char_t *key,
void *structPtr,
const scriptvalue_t *inValue
);

View File

@@ -8,17 +8,25 @@
#include "modulecolor.h" #include "modulecolor.h"
#include "display/color.h" #include "display/color.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "script/struct/scriptstruct.h"
#include "util/string.h" #include "util/string.h"
void moduleColor(scriptcontext_t *context) { void moduleColor(scriptcontext_t *context) {
assertNotNull(context, "Context cannot be NULL."); assertNotNull(context, "Context cannot be NULL.");
scriptStructRegister( if(luaL_newmetatable(context->luaState, "color_mt")) {
context, "color_mt", moduleColorGetter, moduleColorSetter // Metatable is new, set __index and __newindex
); lua_pushstring(context->luaState, "__index");
lua_pushcfunction(context->luaState, moduleColorIndex);
lua_settable(context->luaState, -3);
scriptContextRegFunc(context, "color", moduleColorFuncColor); lua_pushstring(context->luaState, "__newindex");
lua_pushcfunction(context->luaState, moduleColorNewIndex);
lua_settable(context->luaState, -3);
lua_pop(context->luaState, 1);
}
lua_register(context->luaState, "color", moduleColorFuncColor);
scriptContextExec(context, COLOR_SCRIPT); scriptContextExec(context, COLOR_SCRIPT);
} }
@@ -42,92 +50,85 @@ int moduleColorFuncColor(lua_State *L) {
colorchannel8_t b = (colorchannel8_t)lua_tonumber(L, 3); colorchannel8_t b = (colorchannel8_t)lua_tonumber(L, 3);
colorchannel8_t a = (colorchannel8_t)lua_tonumber(L, 4); colorchannel8_t a = (colorchannel8_t)lua_tonumber(L, 4);
// Create color_t as lua memory // Create color_t as lua memory, and push metatable
color_t *color = (color_t*)lua_newuserdata(L, sizeof(color_t)); 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->r = r;
color->g = g; color->g = g;
color->b = b; color->b = b;
color->a = a; color->a = a;
// Push color struct
scriptStructPushMetatable(context, "color_mt", color);
return 1; return 1;
} }
void moduleColorGetter( int moduleColorIndex(lua_State *L) {
const scriptcontext_t *context, assertNotNull(L, "Lua state cannot be NULL.");
const char_t *key,
const void *structPtr, const char_t *key = lua_tostring(L, 2);
scriptvalue_t *outValue assertStrLenMin(key, 1, "Key cannot be empty.");
) {
const color_t *color = (color_t*)structPtr; 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) { if(stringCompare(key, "r") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_INT; lua_pushinteger(L, color->r);
outValue->value.intValue = color->r; return 1;
return;
} }
if(stringCompare(key, "g") == 0) { if(stringCompare(key, "g") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_INT; lua_pushinteger(L, color->g);
outValue->value.intValue = color->g; return 1;
return;
} }
if(stringCompare(key, "b") == 0) { if(stringCompare(key, "b") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_INT; lua_pushinteger(L, color->b);
outValue->value.intValue = color->b; return 1;
return;
} }
if(stringCompare(key, "a") == 0) { if(stringCompare(key, "a") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_INT; lua_pushinteger(L, color->a);
outValue->value.intValue = color->a; return 1;
return;
} }
lua_pushnil(L);
return 1;
} }
void moduleColorSetter( int moduleColorNewIndex(lua_State *L) {
const scriptcontext_t *context, assertNotNull(L, "Lua state cannot be NULL.");
const char_t *key,
void *structPtr, const char_t *key = lua_tostring(L, 2);
const scriptvalue_t *inValue assertStrLenMin(key, 1, "Key cannot be empty.");
) {
color_t *color = (color_t*)structPtr; color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt");
assertNotNull(color, "Color struct cannot be NULL.");
if(stringCompare(key, "r") == 0) { if(stringCompare(key, "r") == 0) {
if(inValue->type == SCRIPT_VALUE_TYPE_INT) { if(!lua_isnumber(L, 3)) {
color->r = (colorchannel8_t)inValue->value.intValue; return luaL_error(L, "color channel r must be a number.");
} else if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) {
color->r = (colorchannel8_t)(inValue->value.floatValue);
} else {
assertUnreachable("Invalid type for color channel r");
} }
return; color->r = (colorchannel8_t)lua_tonumber(L, 3);
return 0;
} else if(stringCompare(key, "g") == 0) { } else if(stringCompare(key, "g") == 0) {
if(inValue->type == SCRIPT_VALUE_TYPE_INT) { if(!lua_isnumber(L, 3)) {
color->g = (colorchannel8_t)inValue->value.intValue; return luaL_error(L, "color channel g must be a number.");
} else if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) {
color->g = (colorchannel8_t)(inValue->value.floatValue);
} else {
assertUnreachable("Invalid type for color channel g");
} }
return; color->g = (colorchannel8_t)lua_tonumber(L, 3);
return 0;
} else if(stringCompare(key, "b") == 0) { } else if(stringCompare(key, "b") == 0) {
if(inValue->type == SCRIPT_VALUE_TYPE_INT) { if(!lua_isnumber(L, 3)) {
color->b = (colorchannel8_t)inValue->value.intValue; return luaL_error(L, "color channel b must be a number.");
} else if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) {
color->b = (colorchannel8_t)(inValue->value.floatValue);
} else {
assertUnreachable("Invalid type for color channel b");
} }
return; color->b = (colorchannel8_t)lua_tonumber(L, 3);
return 0;
} else if(stringCompare(key, "a") == 0) { } else if(stringCompare(key, "a") == 0) {
if(inValue->type == SCRIPT_VALUE_TYPE_INT) { if(!lua_isnumber(L, 3)) {
color->a = (colorchannel8_t)inValue->value.intValue; return luaL_error(L, "color channel a must be a number.");
} else if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) {
color->a = (colorchannel8_t)(inValue->value.floatValue);
} else {
assertUnreachable("Invalid type for color channel a");
} }
return; color->a = (colorchannel8_t)lua_tonumber(L, 3);
return 0;
} }
luaL_error(L, "Invalid color channel key.");
return 0;
} }

View File

@@ -24,31 +24,16 @@ void moduleColor(scriptcontext_t *context);
int moduleColorFuncColor(lua_State *L); int moduleColorFuncColor(lua_State *L);
/** /**
* Getter function for the color structure. * Index function for the color structure.
* * @param L The Lua state.
* @param context The script context. * @return Number of return values.
* @param key The key to get.
* @param structPtr Pointer to the color structure.
* @param outValue Output value.
*/ */
void moduleColorGetter( int moduleColorIndex(lua_State *L);
const scriptcontext_t *context,
const char_t *key,
const void *structPtr,
scriptvalue_t *outValue
);
/** /**
* Setter function for the color structure. * New index function for the color structure.
* *
* @param context The script context. * @param L The Lua state.
* @param key The key to set. * @return Number of return values.
* @param structPtr Pointer to the color structure.
* @param inValue Input value.
*/ */
void moduleColorSetter( int moduleColorNewIndex(lua_State *L);
const scriptcontext_t *context,
const char_t *key,
void *structPtr,
const scriptvalue_t *inValue
);

View File

@@ -7,46 +7,45 @@
#include "moduleglm.h" #include "moduleglm.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "script/struct/scriptstruct.h"
#include "util/string.h" #include "util/string.h"
#include "util/memory.h" #include "util/memory.h"
void moduleGLM(scriptcontext_t *context) { void moduleGLM(scriptcontext_t *context) {
assertNotNull(context, "Context cannot be NULL."); assertNotNull(context, "Context cannot be NULL.");
scriptStructRegister( // scriptStructRegister(
context, // context,
"vec3_mt", // "vec3_mt",
moduleGLMVec3Getter, // moduleGLMVec3Getter,
NULL // NULL
); // );
} }
void moduleGLMVec3Getter( // void moduleGLMVec3Getter(
const scriptcontext_t *context, // const scriptcontext_t *context,
const char_t *key, // const char_t *key,
const void *structPtr, // const void *structPtr,
scriptvalue_t *outValue // scriptvalue_t *outValue
) { // ) {
assertNotNull(context, "Script context cannot be NULL."); // assertNotNull(context, "Script context cannot be NULL.");
assertNotNull(key, "Key cannot be NULL."); // assertNotNull(key, "Key cannot be NULL.");
assertNotNull(structPtr, "Structure pointer cannot be NULL."); // assertNotNull(structPtr, "Structure pointer cannot be NULL.");
assertNotNull(outValue, "Output value cannot be NULL."); // assertNotNull(outValue, "Output value cannot be NULL.");
printf("Getting vec3 field %s\n", key); // printf("Getting vec3 field %s\n", key);
vec3 *v = (vec3 *)structPtr; // vec3 *v = (vec3 *)structPtr;
if(stringCompare(key, "x") == 0) { // if(stringCompare(key, "x") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; // outValue->type = SCRIPT_VALUE_TYPE_FLOAT;
outValue->value.floatValue = (*v)[0]; // outValue->value.floatValue = (*v)[0];
return; // return;
} else if(stringCompare(key, "y") == 0) { // } else if(stringCompare(key, "y") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; // outValue->type = SCRIPT_VALUE_TYPE_FLOAT;
outValue->value.floatValue = (*v)[1]; // outValue->value.floatValue = (*v)[1];
return; // return;
} else if(stringCompare(key, "z") == 0) { // } else if(stringCompare(key, "z") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; // outValue->type = SCRIPT_VALUE_TYPE_FLOAT;
outValue->value.floatValue = (*v)[2]; // outValue->value.floatValue = (*v)[2];
return; // return;
} // }
} // }

View File

@@ -10,9 +10,9 @@
#include "assert/assert.h" #include "assert/assert.h"
void moduleSpriteBatch(scriptcontext_t *context) { void moduleSpriteBatch(scriptcontext_t *context) {
scriptContextRegFunc(context, "spriteBatchFlush", moduleSpriteBatchFlush); lua_register(context->luaState, "spriteBatchFlush", moduleSpriteBatchFlush);
scriptContextRegFunc(context, "spriteBatchClear", moduleSpriteBatchClear); lua_register(context->luaState, "spriteBatchClear", moduleSpriteBatchClear);
scriptContextRegFunc(context, "spriteBatchPush", moduleSpriteBatchPush); lua_register(context->luaState, "spriteBatchPush", moduleSpriteBatchPush);
} }
int moduleSpriteBatchFlush(lua_State *L) { int moduleSpriteBatchFlush(lua_State *L) {
@@ -47,11 +47,10 @@ int moduleSpriteBatchPush(lua_State *L) {
// Color (struct) or nil for white // Color (struct) or nil for white
color_t *color = NULL; color_t *color = NULL;
if(lua_isuserdata(L, 6)) { if(lua_gettop(L) < 6 || lua_isnil(L, 6)) {
color = *(color_t**)lua_touserdata(L, 6); // Allow NULL
assertNotNull(color, "Color struct cannot be NULL"); } else if(lua_isuserdata(L, 6)) {
} else if(lua_isnil(L, 6)) { color = (color_t*)luaL_checkudata(L, 6, "color_mt");
// Extrapolate later.
} else { } else {
return luaL_error(L, "Sprite color must be a color struct or nil"); return luaL_error(L, "Sprite color must be a color struct or nil");
} }

View File

@@ -41,5 +41,5 @@ int moduleEventSubscribe(lua_State *L) {
void moduleEvent(scriptcontext_t *context) { void moduleEvent(scriptcontext_t *context) {
// Reg functions // Reg functions
scriptContextRegFunc(context, "eventSubscribe", moduleEventSubscribe); lua_register(context->luaState, "eventSubscribe", moduleEventSubscribe);
} }

View File

@@ -9,7 +9,6 @@
#include "input/input.h" #include "input/input.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/string.h" #include "util/string.h"
#include "script/struct/scriptstruct.h"
void moduleInput(scriptcontext_t *context) { void moduleInput(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
@@ -37,27 +36,39 @@ void moduleInput(scriptcontext_t *context) {
); );
// Script structure // Script structure
scriptStructRegister(context, "input_mt", &moduleInputEventGetter, NULL); if(luaL_newmetatable(context->luaState, "input_mt")) {
// Metatable methods
lua_pushcfunction(context->luaState, moduleInputIndex);
lua_setfield(context->luaState, -2, "__index");
}
// Events // Events
scriptContextRegPointer(context,"INPUT_EVENT_PRESSED",&INPUT.eventPressed); lua_pushlightuserdata(context->luaState, &INPUT.eventPressed);
scriptContextRegPointer(context,"INPUT_EVENT_RELEASED",&INPUT.eventReleased); lua_setglobal(context->luaState, "INPUT_EVENT_PRESSED");
lua_pushlightuserdata(context->luaState, &INPUT.eventReleased);
lua_setglobal(context->luaState, "INPUT_EVENT_RELEASED");
// Bind methods // Bind methods
scriptContextRegFunc(context, "inputBind", moduleInputBind); lua_register(context->luaState, "inputBind", moduleInputBind);
} }
void moduleInputEventGetter( int moduleInputIndex(lua_State *l) {
const scriptcontext_t *context, assertNotNull(l, "Lua state cannot be NULL");
const char_t *key,
const void *structPtr, const char_t *key = luaL_checkstring(l, 2);
scriptvalue_t *outValue assertStrLenMin(key, 1, "Key cannot be empty.");
) {
if(stringCompare(key, "action") == 0) { if(stringCompare(key, "action") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_INT; lua_pushinteger(
outValue->value.intValue = ((const inputevent_t*)structPtr)->action; l,
return; ((const inputevent_t*)lua_touserdata(l, 1))->action
);
return 1;
} }
return 0;
} }
int moduleInputBind(lua_State *L) { int moduleInputBind(lua_State *L) {

View File

@@ -16,19 +16,11 @@
void moduleInput(scriptcontext_t *context); void moduleInput(scriptcontext_t *context);
/** /**
* Script event getter for input events. * Getter for input structure fields.
* *
* @param context The script context. * @param l The Lua state.
* @param key The key to get.
* @param structPtr Pointer to the input event struct.
* @param outValue Output script value.
*/ */
void moduleInputEventGetter( int moduleInputIndex(lua_State *l);
const scriptcontext_t *context,
const char_t *key,
const void *structPtr,
scriptvalue_t *outValue
);
/** /**
* Script binding for binding an input button to an action. * Script binding for binding an input button to an action.

View File

@@ -17,19 +17,20 @@ void moduleItem(scriptcontext_t *context) {
scriptContextExec(context, ITEM_SCRIPT); scriptContextExec(context, ITEM_SCRIPT);
// Bind BACKPACK const pointer // Bind BACKPACK const pointer
scriptContextRegPointer(context, "BACKPACK", (void *)&BACKPACK); lua_pushlightuserdata(context->luaState, &BACKPACK);
lua_setglobal(context->luaState, "BACKPACK");
// Bind Methods // Bind Methods
scriptContextRegFunc( lua_register(
context, "inventoryItemExists", moduleInventoryItemExists context->luaState, "inventoryItemExists", moduleInventoryItemExists
); );
scriptContextRegFunc(context, "inventoryAdd", moduleInventoryAdd); lua_register(context->luaState, "inventoryAdd", moduleInventoryAdd);
scriptContextRegFunc(context, "inventorySet", moduleInventorySet); lua_register(context->luaState, "inventorySet", moduleInventorySet);
scriptContextRegFunc(context, "inventoryRemove", moduleInventoryRemove); lua_register(context->luaState, "inventoryRemove", moduleInventoryRemove);
scriptContextRegFunc(context, "inventoryGetCount", moduleInventoryGetCount); lua_register(context->luaState, "inventoryGetCount", moduleInventoryGetCount);
scriptContextRegFunc(context, "inventoryIsFull", moduleInventoryIsFull); lua_register(context->luaState, "inventoryIsFull", moduleInventoryIsFull);
scriptContextRegFunc(context, "inventoryItemFull", moduleInventoryItemFull); lua_register(context->luaState, "inventoryItemFull", moduleInventoryItemFull);
scriptContextRegFunc(context, "inventorySort", moduleInventorySort); lua_register(context->luaState, "inventorySort", moduleInventorySort);
} }
int moduleInventoryItemExists(lua_State *L) { int moduleInventoryItemExists(lua_State *L) {

View File

@@ -15,9 +15,9 @@ void moduleLocale(scriptcontext_t *context) {
// Execute the locale script definitions. // Execute the locale script definitions.
scriptContextExec(context, LOCALE_SCRIPT); scriptContextExec(context, LOCALE_SCRIPT);
scriptContextRegFunc(context, "localeGet", moduleLocaleGet); lua_register(context->luaState, "localeGet", moduleLocaleGet);
scriptContextRegFunc(context, "localeSet", moduleLocaleSet); lua_register(context->luaState, "localeSet", moduleLocaleSet);
scriptContextRegFunc(context, "localeGetName", moduleLocaleGetName); lua_register(context->luaState, "localeGetName", moduleLocaleGetName);
} }
int moduleLocaleGet(lua_State *L) { int moduleLocaleGet(lua_State *L) {

View File

@@ -12,7 +12,7 @@
void moduleScene(scriptcontext_t *ctx) { void moduleScene(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL"); assertNotNull(ctx, "Script context cannot be NULL");
scriptContextRegFunc(ctx, "sceneSet", moduleSceneSet); lua_register(ctx->luaState, "sceneSet", moduleSceneSet);
} }
int moduleSceneSet(lua_State *L) { int moduleSceneSet(lua_State *L) {

View File

@@ -14,9 +14,9 @@
void moduleSystem(scriptcontext_t *context) { void moduleSystem(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
scriptContextRegFunc(context, "print", moduleSysPrint); lua_register(context->luaState, "print", moduleSysPrint);
scriptContextRegFunc(context, "include", moduleSysInclude); lua_register(context->luaState, "include", moduleSysInclude);
scriptContextRegFunc(context, "module", moduleSysModule); lua_register(context->luaState, "module", moduleSysModule);
} }
int moduleSysPrint(lua_State *L) { int moduleSysPrint(lua_State *L) {

View File

@@ -7,31 +7,30 @@
#include "moduletime.h" #include "moduletime.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "script/struct/scriptstruct.h"
void moduleTime(scriptcontext_t *ctx) { void moduleTime(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL"); assertNotNull(ctx, "Script context cannot be NULL");
// Script structure // Script structure
scriptStructRegister(ctx, "time_mt", moduleTimeGetter, NULL); if(luaL_newmetatable(ctx->luaState, "time_mt")) {
// Metatable is new, set __index
// Register struct lua_pushstring(ctx->luaState, "__index");
scriptStructPush(ctx, "time_mt", "TIME", &TIME); lua_pushcfunction(ctx->luaState, moduleTimeIndex);
lua_settable(ctx->luaState, -3);
}
} }
void moduleTimeGetter( int moduleTimeIndex(lua_State *L) {
const scriptcontext_t *context, assertNotNull(L, "Lua state cannot be NULL.");
const char_t *key,
const void *structPtr, const char_t *key = lua_tostring(L, 2);
scriptvalue_t *outValue assertStrLenMin(key, 1, "Key cannot be empty.");
) {
if(stringCompare(key, "delta") == 0) { if(stringCompare(key, "delta") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(L, TIME.delta);
outValue->value.floatValue = TIME.delta; return 1;
return;
} else if(stringCompare(key, "time") == 0) { } else if(stringCompare(key, "time") == 0) {
outValue->type = SCRIPT_VALUE_TYPE_FLOAT; lua_pushnumber(L, TIME.time);
outValue->value.floatValue = TIME.time; return 1;
return;
} }
} }

View File

@@ -18,16 +18,8 @@
void moduleTime(scriptcontext_t *ctx); void moduleTime(scriptcontext_t *ctx);
/** /**
* Getter function for the TIME struct in scripts. * Index function for the time structure.
* * @param L The Lua state.
* @param context The script context. * @return Number of return values.
* @param key The key being accessed.
* @param structPtr Pointer to the struct instance.
* @param outValue Output parameter to store the retrieved value.
*/ */
void moduleTimeGetter( int moduleTimeIndex(lua_State *L);
const scriptcontext_t *context,
const char_t *key,
const void *structPtr,
scriptvalue_t *outValue
);

View File

@@ -7,9 +7,30 @@
#include "moduleui.h" #include "moduleui.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "ui/ui.h"
void moduleUi(scriptcontext_t *ctx) { void moduleUi(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL"); assertNotNull(ctx, "Script context cannot be NULL");
lua_register(ctx->luaState, "uiPush", moduleUiPush);
}
int moduleUiPush(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
// Expect path string. TODO Support non scripted ui elements.
if(!lua_isstring(L, 1)) {
return luaL_error(L, "uiPush expects a string path as the first argument");
}
const char_t *path = lua_tostring(L, 1);
printf("Pushing UI from script: %s\n", path);
errorret_t err = uiPushScript(path);
if(err.code != ERROR_OK) {
errorCatch(errorPrint(err));
return luaL_error(L, "uiPush failed for path: %s", path);
}
return 0;
} }

View File

@@ -13,4 +13,14 @@
* *
* @param ctx The script context to register the module in. * @param ctx The script context to register the module in.
*/ */
void moduleUi(scriptcontext_t *ctx); void moduleUi(scriptcontext_t *ctx);
/**
* Lua binding for uiPush function.
*
* Expects a string path to the UI script as the first argument.
*
* @param L The Lua state.
* @return Number of return values on the Lua stack.
*/
int moduleUiPush(lua_State *L);

View File

@@ -38,31 +38,6 @@ errorret_t scriptContextInit(scriptcontext_t *context) {
errorOk(); errorOk();
} }
void scriptContextRegFunc(
scriptcontext_t *context,
const char_t *fnName,
lua_CFunction function
) {
assertNotNull(context, "Script context cannot be NULL");
assertNotNull(fnName, "Function name cannot be NULL");
assertNotNull(function, "Function cannot be NULL");
lua_register(context->luaState, fnName, function);
}
void scriptContextRegPointer(
scriptcontext_t *context,
const char_t *name,
void *pointer
) {
assertNotNull(context, "Script context cannot be NULL");
assertNotNull(name, "Name cannot be NULL");
assertNotNull(pointer, "Pointer cannot be NULL");
lua_pushlightuserdata(context->luaState, pointer);
lua_setglobal(context->luaState, name);
}
errorret_t scriptContextCallFunc( errorret_t scriptContextCallFunc(
scriptcontext_t *context, scriptcontext_t *context,
const char_t *fnName, const char_t *fnName,

View File

@@ -29,32 +29,6 @@ typedef struct scriptcontext_s {
*/ */
errorret_t scriptContextInit(scriptcontext_t *context); errorret_t scriptContextInit(scriptcontext_t *context);
/**
* Register a C function within a script context.
*
* @param context The script context to use.
* @param fnName The name of the function in Lua.
* @param function The C function to register.
*/
void scriptContextRegFunc(
scriptcontext_t *context,
const char_t *fnName,
lua_CFunction function
);
/**
* Register a pointer within a script context.
*
* @param context The script context to use.
* @param name The name of the pointer in Lua.
* @param pointer The pointer to register.
*/
void scriptContextRegPointer(
scriptcontext_t *context,
const char_t *name,
void *pointer
);
/** /**
* Call a Lua function within a script context. * Call a Lua function within a script context.
* *

View File

@@ -1,191 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scriptstruct.h"
#include "assert/assert.h"
#include "util/memory.h"
void scriptStructRegister(
scriptcontext_t *context,
const char_t *metatableName,
const scriptstructgetter_t getter,
const scriptstructsetter_t setter
) {
assertNotNull(context, "Script context cannot be NULL");
assertNotNull(metatableName, "Metatable name cannot be NULL");
// Create metatable
if(!luaL_newmetatable(context->luaState, metatableName)) return;
// Create a Lua owned structure for holding the metatable context.
structmetatablecontext_t *metaContext = lua_newuserdata(
context->luaState,
sizeof(structmetatablecontext_t)
);
metaContext->context = context;
metaContext->getter = getter;
metaContext->setter = setter;
// Store in the metatable.
lua_setfield(context->luaState, -2, "__structmetatablecontext");
// Set __index and __newindex metamethods for Lua.
lua_pushcfunction(context->luaState, scriptStructIndex);
lua_setfield(context->luaState, -2, "__index");
lua_pushcfunction(context->luaState, scriptStructNewIndex);
lua_setfield(context->luaState, -2, "__newindex");
}
int scriptStructIndex(lua_State *l) {
structmetatablecontext_t *ctx = scriptStructGetMetatableContext(l);
if(!ctx->getter) {
luaL_error(l, "Attempt to read from write-only structure field");
return 0;
}
const char_t *key = lua_tostring(l, 2);
void *structPtr = *(void **)lua_touserdata(l, 1);
assertNotNull(structPtr, "Structure pointer cannot be NULL");
scriptvalue_t outValue = { .type = SCRIPT_VALUE_TYPE_NIL };
ctx->getter(ctx->context, key, structPtr, &outValue);
switch(outValue.type) {
case SCRIPT_VALUE_TYPE_INT:
lua_pushinteger(l, outValue.value.intValue);
break;
case SCRIPT_VALUE_TYPE_FLOAT:
lua_pushnumber(l, outValue.value.floatValue);
break;
case SCRIPT_VALUE_TYPE_STRING:
assertNotNull(outValue.value.strValue, "String value cannot be NULL");
lua_pushstring(l, outValue.value.strValue);
memoryFree((void *)outValue.value.strValue);
break;
case SCRIPT_VALUE_TYPE_BOOL:
lua_pushboolean(l, outValue.value.boolValue);
break;
case SCRIPT_VALUE_TYPE_NIL:
lua_pushnil(l);
break;
case SCRIPT_VALUE_TYPE_USERDATA:
break;
default:
assertUnreachable("Unsupported value type for struct field retrieval");
break;
}
return 1;
}
int scriptStructNewIndex(lua_State *l) {
structmetatablecontext_t *ctx = scriptStructGetMetatableContext(l);
if(ctx->setter == NULL) {
luaL_error(l, "Attempt to set read-only structure field");
return 0;
}
const char_t *key = lua_tostring(l, 2);
void *structPtr = *(void **)lua_touserdata(l, 1);
assertNotNull(structPtr, "Structure pointer cannot be NULL");
scriptvalue_t inValue;
int t = lua_type(l, 3);
switch(t) {
case LUA_TNUMBER:
if(lua_isinteger(l, 3)) {
inValue.type = SCRIPT_VALUE_TYPE_INT;
inValue.value.intValue = (int32_t)lua_tointeger(l, 3);
} else {
inValue.type = SCRIPT_VALUE_TYPE_FLOAT;
inValue.value.floatValue = (float)lua_tonumber(l, 3);
}
break;
case LUA_TSTRING:
inValue.type = SCRIPT_VALUE_TYPE_STRING;
inValue.value.strValue = lua_tostring(l, 3);
break;
case LUA_TBOOLEAN:
inValue.type = SCRIPT_VALUE_TYPE_BOOL;
inValue.value.boolValue = lua_toboolean(l, 3);
break;
case LUA_TNIL:
inValue.type = SCRIPT_VALUE_TYPE_NIL;
break;
default:
assertUnreachable("Unsupported value type for struct field assignment");
break;
}
ctx->setter(ctx->context, key, structPtr, &inValue);
lua_pushnil(l);
return 1;
}
structmetatablecontext_t * scriptStructGetMetatableContext(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_getmetatable(L, 1)) {
assertUnreachable("Expected metatable on structure");
}
lua_getfield(L, -1, "__structmetatablecontext");
structmetatablecontext_t *metaContext = (
(structmetatablecontext_t *)lua_touserdata(L, -1)
);
assertNotNull(metaContext, "Metatable context userdata cannot be NULL");
lua_pop(L, 2);
return metaContext;
}
void scriptStructPushMetatable(
const scriptcontext_t *context,
const char_t *metatableName,
void *structPtr
) {
assertNotNull(context, "Script context cannot be NULL");
assertNotNull(metatableName, "Metatable name cannot be NULL");
assertNotNull(structPtr, "Structure pointer cannot be NULL");
// Create userdata
void **ud = (void **)lua_newuserdata(context->luaState, sizeof(void *));
*ud = structPtr;
// Set metatable
luaL_getmetatable(context->luaState, metatableName);
lua_setmetatable(context->luaState, -2);
}
void scriptStructPush(
scriptcontext_t *context,
const char_t *metatableName,
const char_t *variableName,
void *structPtr
) {
assertNotNull(context, "Script context cannot be NULL");
assertNotNull(metatableName, "Metatable name cannot be NULL");
assertNotNull(variableName, "Variable name cannot be NULL");
assertNotNull(structPtr, "Structure pointer cannot be NULL");
scriptStructPushMetatable(context, metatableName, structPtr);
lua_setglobal(context->luaState, variableName);
}

View File

@@ -1,86 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "scriptstructgetter.h"
#include "scriptstructsetter.h"
typedef struct {
scriptcontext_t *context;
scriptstructgetter_t getter;
scriptstructsetter_t setter;
} structmetatablecontext_t;
/**
* Registers a script structure to allow a script to access its fields. You will
* be creating a new metatable in Lua with the given name, so Ideally use a name
* like Ideally struct_mt e.g. player_t or entity_t.
*
* @param context The script context.
* @param metatableName The name of the metatable to register
* @param getter The getter function for retrieving field values.
* @param setter The setter function for setting field values.
*/
void scriptStructRegister(
scriptcontext_t *context,
const char_t *metatableName,
const scriptstructgetter_t getter,
const scriptstructsetter_t setter
);
/**
* Callback received from Lua to index (get) a structure field.
*
* @param l The Lua state.
* @return The number of return values.
*/
int scriptStructIndex(lua_State *l);
/**
* Callback received from Lua to newindex (set) a structure field.
*
* @param l The Lua state.
* @return The number of return values.
*/
int scriptStructNewIndex(lua_State *l);
/**
* Pops the metatable context from the given Lua state. This is only valid from
* within the index or newindex methods.
*
* @param l The Lua state.
* @return The metatable context.
*/
structmetatablecontext_t *scriptStructGetMetatableContext(lua_State *l);
/**
* Pushes a structure metatable onto the Lua stack.
*
* @param context The script context.
* @param metatableName The name of the metatable to associate with.
* @param structPtr Pointer to the structure to push.
*/
void scriptStructPushMetatable(
const scriptcontext_t *context,
const char_t *metatableName,
void *structPtr
);
/**
* Pushes a structure onto the Lua stack and assigns it to a global variable.
*
* @param context The script context.
* @param metatableName The name of the metatable to associate with.
* @param variableName The name of the global variable to assign to.
* @param structPtr Pointer to the structure to push.
*/
void scriptStructPush(
scriptcontext_t *context,
const char_t *metatableName,
const char_t *variableName,
void *structPtr
);

View File

@@ -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"
typedef void (*scriptstructgetter_t)(
const scriptcontext_t *context,
const char_t *key,
const void *structPtr,
scriptvalue_t *outValue
);

View File

@@ -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"
typedef void (*scriptstructsetter_t)(
const scriptcontext_t *context,
const char_t *key,
void *structPtr,
const scriptvalue_t *inValue
);

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2025 Dominic Masters # Copyright (c) 2026 Dominic Masters
# #
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
@@ -7,11 +7,5 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
ui.c ui.c
uitext.c uielement.c
uidebug.c )
uiframe.c
uitextbox.c
)
# Subdirs
add_subdirectory(element)

View File

@@ -1,67 +1,68 @@
/** /**
* Copyright (c) 2025 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#include "ui.h" #include "ui.h"
#include "assert/assert.h"
#include "ui/uidebug.h"
#include "util/memory.h" #include "util/memory.h"
#include "display/tileset/tileset_minogram.h" #include "assert/assert.h"
#include "display/screen.h" #include "display/spritebatch.h"
// #include "ui/uitextbox.h"
ui_t UI; ui_t UI;
errorret_t uiInit(void) { errorret_t uiInit(void) {
memoryZero(&UI, sizeof(ui_t)); memoryZero(&UI, sizeof(ui_t));
cameraInitOrthographic(&UI.camera); cameraInitOrthographic(&UI.camera);
// Initialize UI components here
uiSetFont(&TILESET_MINOGRAM);
errorOk(); errorOk();
} }
void uiUpdate(void) { void uiUpdate(void) {
// Update UI state here uint_fast8_t i = 0;
UI.camera.orthographic.left = 0; while(i < UI.stackSize) {
UI.camera.orthographic.right = SCREEN.width; errorCatch(errorPrint(uiElementUpdate(&UI.stack[i])));
UI.camera.orthographic.top = 0; i++;
UI.camera.orthographic.bottom = SCREEN.height; }
// uiTextboxUpdate();
} }
void uiRender(void) { void uiRender(void) {
cameraPushMatrix(&UI.camera); cameraPushMatrix(&UI.camera);
spriteBatchClear();
// Render UI elements here
if(UI.fontTexture.width > 0) { uint_fast8_t i = 0;
uiDebugRender(UI.fontTileset, &UI.fontTexture); while(i < UI.stackSize) {
errorCatch(errorPrint(uiElementRender(&UI.stack[i])));
i++;
} }
// spriteBatchPush(
// NULL,
// 0, 0, 32, 32, COLOR_RED, 0, 0, 1, 1
// );
spriteBatchFlush();
cameraPopMatrix(); cameraPopMatrix();
} }
errorret_t uiSetFont(const tileset_t *fontTileset) { errorret_t uiPushScript(const char_t *path) {
if(UI.fontTexture.width > 0) { assertTrue(UI.stackSize < UI_STACK_SIZE, "UI stack overflow");
textureDispose(&UI.fontTexture); uielement_t *element = &UI.stack[UI.stackSize];
UI.fontTexture.width = -1; errorChain(uiElementInitScript(element, path));
} UI.stackSize++;
// Ret Id or something?
assertNotNull(fontTileset, "Font tileset cannot be NULL.");
UI.fontTileset = fontTileset;
errorChain(assetLoad(UI.fontTileset->image, &UI.fontTexture));
errorOk(); errorOk();
} }
void uiPop(void) {
assertTrue(UI.stackSize > 0, "UI stack underflow");
UI.stackSize--;
uielement_t *element = &UI.stack[UI.stackSize];
uiElementDispose(element);
}
void uiDispose(void) { void uiDispose(void) {
if(UI.fontTexture.width > 0) { while(UI.stackSize > 0) {
textureDispose(&UI.fontTexture); uiPop();
UI.fontTexture.width = -1;
} }
} }

View File

@@ -1,51 +1,53 @@
/** /**
* Copyright (c) 2025 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "asset/asset.h"
#include "display/texture.h"
#include "display/tileset/tileset.h"
#include "display/camera/camera.h" #include "display/camera/camera.h"
#include "ui/uielement.h"
#define UI_STACK_SIZE 64
typedef struct { typedef struct {
camera_t camera; camera_t camera;
texture_t fontTexture;
const tileset_t *fontTileset; uielement_t stack[UI_STACK_SIZE];
uint_fast8_t stackSize;
} ui_t; } ui_t;
extern ui_t UI; extern ui_t UI;
/** /**
* Initializes the UI system, loading necessary resources. * Initializes the UI system.
*
* @return An errorret_t indicating success or failure.
*/ */
errorret_t uiInit(void); errorret_t uiInit(void);
/** /**
* Updates the UI state, handling user interactions and animations. * Updates the UI system.
*/ */
void uiUpdate(void); void uiUpdate(void);
/** /**
* Renders the UI elements to the screen. * Renders the UI system.
*/ */
void uiRender(void); void uiRender(void);
/** /**
* Sets the font tileset for UI text rendering. * Pushes a new UI element onto the UI stack from a script file.
* *
* @param fontTileset Pointer to the tileset to use for UI fonts. * @param path Path to the UI script file.
*
* @return An errorret_t indicating success or failure.
*/ */
errorret_t uiSetFont(const tileset_t *fontTileset); errorret_t uiPushScript(const char_t *path);
/** /**
* Cleans up and frees all UI resources. * Pops the top UI element from the UI stack.
*/
void uiPop(void);
/**
* Disposes of the UI system.
*/ */
void uiDispose(void); void uiDispose(void);

57
src/ui/uielement.c Normal file
View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uielement.h"
#include "display/screen.h"
errorret_t uiElementInitScript(
uielement_t *element,
const char_t *path
) {
errorChain(scriptContextInit(&element->ctx));
errorChain(scriptContextExecFile(&element->ctx, path));
errorOk();
}
errorret_t uiElementUpdate(uielement_t *element) {
if(!scriptContextHasFunc(&element->ctx, "update")) errorOk();
errorChain(scriptContextCallFunc(
&element->ctx,
"update",
NULL,
0,
NULL
));
errorOk();
}
errorret_t uiElementRender(uielement_t *element) {
if(!scriptContextHasFunc(&element->ctx, "render")) errorOk();
// scriptvalue_t args[4] = {
// { .type = SCRIPT_VALUE_TYPE_FLOAT, .value.floatValue = 0.0f },
// { .type = SCRIPT_VALUE_TYPE_FLOAT, .value.floatValue = 0.0f },
// { .type = SCRIPT_VALUE_TYPE_FLOAT, .value.floatValue = SCREEN.width },
// { .type = SCRIPT_VALUE_TYPE_FLOAT, .value.floatValue = SCREEN.height },
// };
// errorChain(scriptContextCallFunc(
// &element->ctx,
// "render",
// args,
// 4,
// NULL
// ));
errorOk();
}
void uiElementDispose(uielement_t *element) {
scriptContextDispose(&element->ctx);
}

46
src/ui/uielement.h Normal file
View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
// TODO: Support both scripted and native UI elements.
typedef struct {
scriptcontext_t ctx;
} uielement_t;
/**
* Initializes a scripted UI Element.
*
* @param element The UI element to initialize.
* @param path Path to the UI script file.
*/
errorret_t uiElementInitScript(
uielement_t *element,
const char_t *path
);
/**
* Updates/ticks a UI element's logic.
*
* @param element The UI element to tick.
*/
errorret_t uiElementUpdate(uielement_t *element);
/**
* Renders a UI element.
*
* @param element The UI element to render.
*/
errorret_t uiElementRender(uielement_t *element);
/**
* Disposes of a UI element.
*
* @param element The UI element to dispose of.
*/
void uiElementDispose(uielement_t *element);