diff --git a/archive/modulemap.h b/archive/modulemap.h index 5b70729..ce126c6 100644 --- a/archive/modulemap.h +++ b/archive/modulemap.h @@ -45,5 +45,5 @@ int moduleMapLoad(lua_State *L) { void moduleMapSystem(scriptcontext_t *context) { assertNotNull(context, "Script context cannot be NULL"); - scriptContextRegFunc(context, "mapLoad", moduleMapLoad); + lua_register(context->luaState, "mapLoad", moduleMapLoad); } \ No newline at end of file diff --git a/archive/scriptfuncentity.h b/archive/scriptfuncentity.h index dc75f6a..0232df0 100644 --- a/archive/scriptfuncentity.h +++ b/archive/scriptfuncentity.h @@ -128,8 +128,8 @@ int scriptFuncEntitySetZ(lua_State *L) { void scriptFuncEntity(scriptcontext_t *context) { assertNotNull(context, "Script context cannot be NULL"); - scriptContextRegFunc(context, "entityAdd", scriptFuncEntityAdd); - scriptContextRegFunc(context, "entitySetX", scriptFuncEntitySetX); - scriptContextRegFunc(context, "entitySetY", scriptFuncEntitySetY); - scriptContextRegFunc(context, "entitySetZ", scriptFuncEntitySetZ); + lua_register(context->luaState, "entityAdd", scriptFuncEntityAdd); + lua_register(context->luaState, "entitySetX", scriptFuncEntitySetX); + lua_register(context->luaState, "entitySetY", scriptFuncEntitySetY); + lua_register(context->luaState, "entitySetZ", scriptFuncEntitySetZ); } \ No newline at end of file diff --git a/src/script/struct/CMakeLists.txt b/archive/ui/CMakeLists.txt similarity index 51% rename from src/script/struct/CMakeLists.txt rename to archive/ui/CMakeLists.txt index 0be342d..0155c3b 100644 --- a/src/script/struct/CMakeLists.txt +++ b/archive/ui/CMakeLists.txt @@ -1,10 +1,17 @@ -# Copyright (c) 2026 Dominic Masters -# +# Copyright (c) 2025 Dominic Masters +# # This software is released under the MIT License. # https://opensource.org/licenses/MIT # Sources target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - scriptstruct.c -) \ No newline at end of file + ui.c + uitext.c + uidebug.c + uiframe.c + uitextbox.c +) + +# Subdirs +add_subdirectory(element) \ No newline at end of file diff --git a/src/ui/element/CMakeLists.txt b/archive/ui/element/CMakeLists.txt similarity index 100% rename from src/ui/element/CMakeLists.txt rename to archive/ui/element/CMakeLists.txt diff --git a/src/ui/element/uielementtype.h b/archive/ui/element/uielementtype.h similarity index 100% rename from src/ui/element/uielementtype.h rename to archive/ui/element/uielementtype.h diff --git a/archive/ui/ui.c b/archive/ui/ui.c new file mode 100644 index 0000000..c6bf883 --- /dev/null +++ b/archive/ui/ui.c @@ -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; + } +} \ No newline at end of file diff --git a/archive/ui/ui.h b/archive/ui/ui.h new file mode 100644 index 0000000..956fe45 --- /dev/null +++ b/archive/ui/ui.h @@ -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); \ No newline at end of file diff --git a/src/ui/uidebug.c b/archive/ui/uidebug.c similarity index 100% rename from src/ui/uidebug.c rename to archive/ui/uidebug.c diff --git a/src/ui/uidebug.h b/archive/ui/uidebug.h similarity index 100% rename from src/ui/uidebug.h rename to archive/ui/uidebug.h diff --git a/src/ui/uiframe.c b/archive/ui/uiframe.c similarity index 100% rename from src/ui/uiframe.c rename to archive/ui/uiframe.c diff --git a/src/ui/uiframe.h b/archive/ui/uiframe.h similarity index 100% rename from src/ui/uiframe.h rename to archive/ui/uiframe.h diff --git a/src/ui/uitext.c b/archive/ui/uitext.c similarity index 100% rename from src/ui/uitext.c rename to archive/ui/uitext.c diff --git a/src/ui/uitext.h b/archive/ui/uitext.h similarity index 100% rename from src/ui/uitext.h rename to archive/ui/uitext.h diff --git a/src/ui/uitextbox.c b/archive/ui/uitextbox.c similarity index 100% rename from src/ui/uitextbox.c rename to archive/ui/uitextbox.c diff --git a/src/ui/uitextbox.h b/archive/ui/uitextbox.h similarity index 100% rename from src/ui/uitextbox.h rename to archive/ui/uitextbox.h diff --git a/src/ui2/CMakeLists.txt b/archive/ui2/CMakeLists.txt similarity index 100% rename from src/ui2/CMakeLists.txt rename to archive/ui2/CMakeLists.txt diff --git a/src/ui2/ui2.c b/archive/ui2/ui2.c similarity index 100% rename from src/ui2/ui2.c rename to archive/ui2/ui2.c diff --git a/src/ui2/ui2.h b/archive/ui2/ui2.h similarity index 100% rename from src/ui2/ui2.h rename to archive/ui2/ui2.h diff --git a/assets/scene/initial.lua b/assets/scene/initial.lua index e58bfaf..0abb998 100644 --- a/assets/scene/initial.lua +++ b/assets/scene/initial.lua @@ -1,21 +1,17 @@ module('spritebatch') -module('time') +-- module('time') module('camera') -module('glm') +-- module('glm') module('color') camera = cameraCreate(CAMERA_PROJECTION_TYPE_PERSPECTIVE) -color = colorBlue() - function sceneDispose() -- print('Disposing initial scene') end 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 function sceneRender() @@ -25,7 +21,7 @@ function sceneRender() nil, 0, 0, 1, 2, - color + colorBlue() ) spriteBatchFlush() diff --git a/assets/ui/CMakeLists.txt b/assets/ui/CMakeLists.txt index 22dbf0a..d9c9a0f 100644 --- a/assets/ui/CMakeLists.txt +++ b/assets/ui/CMakeLists.txt @@ -3,4 +3,5 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -add_asset(TILESET minogram.png type=PALETTIZED tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering \ No newline at end of file +add_asset(TILESET minogram.png type=PALETTIZED tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering +add_asset(SCRIPT test.lua) \ No newline at end of file diff --git a/assets/ui/test.lua b/assets/ui/test.lua index a6c6ffc..67aa1ca 100644 --- a/assets/ui/test.lua +++ b/assets/ui/test.lua @@ -1,6 +1,9 @@ -module('ui') -module('color') +module('spritebatch') -function draw(x, y) - uiDrawRect(COLOR_WHITE, x, y, 32, 32) +function render(x, y, w, h) + spriteBatchPush( + nil, + x, y, + w, h + ) end \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b6fab3..409cef9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,5 +65,4 @@ add_subdirectory(script) add_subdirectory(thread) add_subdirectory(time) add_subdirectory(ui) -add_subdirectory(ui2) add_subdirectory(util) \ No newline at end of file diff --git a/src/display/camera/camera.h b/src/display/camera/camera.h index c266304..f384760 100644 --- a/src/display/camera/camera.h +++ b/src/display/camera/camera.h @@ -23,8 +23,6 @@ typedef enum { } cameraviewtype_t; typedef struct { - cameraprojectiontype_t projType; - cameraviewtype_t viewType; union { mat4 view; @@ -63,6 +61,9 @@ typedef struct { float_t nearClip; float_t farClip; + + cameraprojectiontype_t projType; + cameraviewtype_t viewType; } camera_t; /** diff --git a/src/event/event.c b/src/event/event.c index a109a63..f0975d5 100644 --- a/src/event/event.c +++ b/src/event/event.c @@ -8,7 +8,6 @@ #include "event.h" #include "assert/assert.h" #include "util/memory.h" -#include "script/struct/scriptstruct.h" void eventInit( event_t *event, @@ -228,10 +227,11 @@ void eventInvoke( lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef); if(eventParams != NULL && metatableName != NULL) { - scriptStructPushMetatable( - listener->user.script.context, - metatableName, - (void *)eventParams + lua_getmetatable(L, -1); + luaL_getmetatable(L, metatableName); + assertTrue( + lua_rawequal(L, -1, -2), + "Event parameter metatable does not match expected type" ); } diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 3ad00e9..e086520 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -12,5 +12,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} ) # Subdirectories -add_subdirectory(module) -add_subdirectory(struct) \ No newline at end of file +add_subdirectory(module) \ No newline at end of file diff --git a/src/script/module/display/modulecamera.c b/src/script/module/display/modulecamera.c index 2669492..7df8ca5 100644 --- a/src/script/module/display/modulecamera.c +++ b/src/script/module/display/modulecamera.c @@ -9,19 +9,20 @@ #include "assert/assert.h" #include "display/camera/camera.h" #include "util/memory.h" -#include "script/struct/scriptstruct.h" #include "util/string.h" void moduleCamera(scriptcontext_t *context) { assertNotNull(context, "Context cannot be NULL."); - // Structure - scriptStructRegister( - context, - "camera_mt", - moduleCameraGetter, - moduleCameraSetter - ); + // Create metatable for camera structure. + if(luaL_newmetatable(context->luaState, "camera_mt")) { + // Metatable methods + lua_pushcfunction(context->luaState, moduleCameraIndex); + lua_setfield(context->luaState, -2, "__index"); + lua_pushcfunction(context->luaState, moduleCameraNewIndex); + lua_setfield(context->luaState, -2, "__newindex"); + lua_pop(context->luaState, 1); + } // Definitions #define MODULE_CAMERA_SCRIPT_LEN 64 @@ -67,9 +68,9 @@ void moduleCamera(scriptcontext_t *context) { ); // Methods - scriptContextRegFunc(context, "cameraCreate", moduleCameraCreate); - scriptContextRegFunc(context, "cameraPushMatrix", moduleCameraPushMatrix); - scriptContextRegFunc(context, "cameraPopMatrix", moduleCameraPopMatrix); + lua_register(context->luaState, "cameraCreate", moduleCameraCreate); + lua_register(context->luaState, "cameraPushMatrix", moduleCameraPushMatrix); + lua_register(context->luaState, "cameraPopMatrix", moduleCameraPopMatrix); } int moduleCameraCreate(lua_State *L) { @@ -84,12 +85,13 @@ int moduleCameraCreate(lua_State *L) { 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)); memoryZero(cam, sizeof(camera_t)); - // Push metatable. - scriptStructPushMetatable(context, "camera_mt", cam); + // Set metatable + luaL_getmetatable(L, "camera_mt"); + lua_setmetatable(L, -2); // Init camera switch(projType) { @@ -114,9 +116,11 @@ int moduleCameraCreate(lua_State *L) { int moduleCameraPushMatrix(lua_State *L) { 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_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."); cameraPushMatrix(cam); @@ -129,64 +133,51 @@ int moduleCameraPopMatrix(lua_State *L) { return 0; } -void moduleCameraGetter( - const scriptcontext_t *context, - 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."); +int moduleCameraIndex(lua_State *l) { + assertNotNull(l, "Lua state 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) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->nearClip; - return; + lua_pushnumber(l, cam->nearClip); + return 1; } else if(stringCompare(key, "far") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->farClip; - return; + lua_pushnumber(l, cam->farClip); + return 1; } else if(stringCompare(key, "nearClip") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->nearClip; - return; + lua_pushnumber(l, cam->nearClip); + return 1; } else if(stringCompare(key, "farClip") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->farClip; - return; + lua_pushnumber(l, cam->farClip); + return 1; } // Perspective relative values if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) { if(stringCompare(key, "left") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->orthographic.left; - return; + lua_pushnumber(l, cam->orthographic.left); + return 1; } else if(stringCompare(key, "right") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->orthographic.right; - return; + lua_pushnumber(l, cam->orthographic.right); + return 1; } else if(stringCompare(key, "top") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->orthographic.top; - return; + lua_pushnumber(l, cam->orthographic.top); + return 1; } else if(stringCompare(key, "bottom") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->orthographic.bottom; - return; + lua_pushnumber(l, cam->orthographic.bottom); + return 1; } } else if( cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE || cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED ) { if(stringCompare(key, "fov") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = cam->perspective.fov; - return; + lua_pushnumber(l, cam->perspective.fov); + return 1; } } @@ -194,107 +185,92 @@ void moduleCameraGetter( if(cam->viewType == CAMERA_VIEW_TYPE_MATRIX) { } else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) { - if(stringCompare(key, "position") == 0) { - scriptStructPushMetatable( - context, - "vec3_mt", - (void*)&cam->lookat.position - ); - outValue->type = SCRIPT_VALUE_TYPE_USERDATA; - return; - } + // TODO: Push vec3 } else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT) { } else if(cam->viewType == CAMERA_VIEW_TYPE_2D) { } + + lua_pushnil(l); + return 1; } -void moduleCameraSetter( - const scriptcontext_t *context, - const char_t *key, - void *structPtr, - const scriptvalue_t *inValue -) { - 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."); +int moduleCameraNewIndex(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL."); + + const char_t *key = luaL_checkstring(l, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); - 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(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; + if(stringCompare(key, "near") == 0 || stringCompare(key, "nearClip") == 0) { + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Camera near clip must be a number."); } - return; - } else if(stringCompare(key, "far") == 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; + cam->nearClip = (float_t)lua_tonumber(l, 3); + return 0; + } + + if(stringCompare(key, "far") == 0 || stringCompare(key, "farClip") == 0) { + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Camera far clip must be a number."); } - return; - } else if(stringCompare(key, "nearClip") == 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; + cam->farClip = (float_t)lua_tonumber(l, 3); + return 0; } // Perspective relative values if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) { if(stringCompare(key, "left") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - cam->orthographic.left = inValue->value.floatValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - cam->orthographic.left = (float_t)inValue->value.intValue; + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Camera orthographic left must be a number."); } - return; - } else if(stringCompare(key, "right") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - cam->orthographic.right = inValue->value.floatValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - cam->orthographic.right = (float_t)inValue->value.intValue; + cam->orthographic.left = (float_t)lua_tonumber(l, 3); + return 0; + } + + if(stringCompare(key, "right") == 0) { + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Camera orthographic right must be a number."); } - return; - } else if(stringCompare(key, "top") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - cam->orthographic.top = inValue->value.floatValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - cam->orthographic.top = (float_t)inValue->value.intValue; + cam->orthographic.right = (float_t)lua_tonumber(l, 3); + return 0; + } + + if(stringCompare(key, "top") == 0) { + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Camera orthographic top must be a number."); } - return; - } else if(stringCompare(key, "bottom") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - cam->orthographic.bottom = inValue->value.floatValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - cam->orthographic.bottom = (float_t)inValue->value.intValue; + cam->orthographic.top = (float_t)lua_tonumber(l, 3); + return 0; + } + + if(stringCompare(key, "bottom") == 0) { + 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( cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE || cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED ) { if(stringCompare(key, "fov") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - cam->perspective.fov = inValue->value.floatValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - cam->perspective.fov = (float_t)inValue->value.intValue; + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Camera perspective FOV must be a number."); } - 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; } \ No newline at end of file diff --git a/src/script/module/display/modulecamera.h b/src/script/module/display/modulecamera.h index 77855b9..7032e53 100644 --- a/src/script/module/display/modulecamera.h +++ b/src/script/module/display/modulecamera.h @@ -42,29 +42,13 @@ int moduleCameraPopMatrix(lua_State *L); /** * Getter for camera structure fields. * - * @param context The script context. - * @param key The field key. - * @param structPtr Pointer to the camera structure. - * @param outValue Output script value. + * @param l The Lua state. */ -void moduleCameraGetter( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -); +int moduleCameraIndex(lua_State *l); /** * Setter for camera structure fields. - * - * @param context The script context. - * @param key The field key. - * @param structPtr Pointer to the camera structure. - * @param inValue Input script value. + * + * @param l The Lua state. */ -void moduleCameraSetter( - const scriptcontext_t *context, - const char_t *key, - void *structPtr, - const scriptvalue_t *inValue -); \ No newline at end of file +int moduleCameraNewIndex(lua_State *l); \ No newline at end of file diff --git a/src/script/module/display/modulecolor.c b/src/script/module/display/modulecolor.c index 086187e..cb2ddf1 100644 --- a/src/script/module/display/modulecolor.c +++ b/src/script/module/display/modulecolor.c @@ -8,17 +8,25 @@ #include "modulecolor.h" #include "display/color.h" #include "assert/assert.h" -#include "script/struct/scriptstruct.h" #include "util/string.h" void moduleColor(scriptcontext_t *context) { assertNotNull(context, "Context cannot be NULL."); - scriptStructRegister( - context, "color_mt", moduleColorGetter, moduleColorSetter - ); + if(luaL_newmetatable(context->luaState, "color_mt")) { + // Metatable is new, set __index and __newindex + lua_pushstring(context->luaState, "__index"); + lua_pushcfunction(context->luaState, moduleColorIndex); + lua_settable(context->luaState, -3); - 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); } @@ -42,92 +50,85 @@ int moduleColorFuncColor(lua_State *L) { colorchannel8_t b = (colorchannel8_t)lua_tonumber(L, 3); colorchannel8_t a = (colorchannel8_t)lua_tonumber(L, 4); - // Create color_t as lua memory - color_t *color = (color_t*)lua_newuserdata(L, sizeof(color_t)); + // Create color_t as lua memory, and push metatable + color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t)); + luaL_getmetatable(L, "color_mt"); + lua_setmetatable(L, -2); + + // Initial values. color->r = r; color->g = g; color->b = b; color->a = a; - // Push color struct - scriptStructPushMetatable(context, "color_mt", color); - return 1; } -void moduleColorGetter( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -) { - const color_t *color = (color_t*)structPtr; +int moduleColorIndex(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + + const char_t *key = lua_tostring(L, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); + + const color_t *color = (const color_t*)luaL_checkudata(L, 1, "color_mt"); + assertNotNull(color, "Color struct cannot be NULL."); if(stringCompare(key, "r") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_INT; - outValue->value.intValue = color->r; - return; + lua_pushinteger(L, color->r); + return 1; } if(stringCompare(key, "g") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_INT; - outValue->value.intValue = color->g; - return; + lua_pushinteger(L, color->g); + return 1; } if(stringCompare(key, "b") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_INT; - outValue->value.intValue = color->b; - return; + lua_pushinteger(L, color->b); + return 1; } if(stringCompare(key, "a") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_INT; - outValue->value.intValue = color->a; - return; + lua_pushinteger(L, color->a); + return 1; } + + lua_pushnil(L); + return 1; } -void moduleColorSetter( - const scriptcontext_t *context, - const char_t *key, - void *structPtr, - const scriptvalue_t *inValue -) { - color_t *color = (color_t*)structPtr; +int moduleColorNewIndex(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + + const char_t *key = lua_tostring(L, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); + + color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt"); + assertNotNull(color, "Color struct cannot be NULL."); if(stringCompare(key, "r") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - color->r = (colorchannel8_t)inValue->value.intValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - color->r = (colorchannel8_t)(inValue->value.floatValue); - } else { - assertUnreachable("Invalid type for color channel r"); + if(!lua_isnumber(L, 3)) { + return luaL_error(L, "color channel r must be a number."); } - return; + color->r = (colorchannel8_t)lua_tonumber(L, 3); + return 0; } else if(stringCompare(key, "g") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - color->g = (colorchannel8_t)inValue->value.intValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - color->g = (colorchannel8_t)(inValue->value.floatValue); - } else { - assertUnreachable("Invalid type for color channel g"); + if(!lua_isnumber(L, 3)) { + return luaL_error(L, "color channel g must be a number."); } - return; + color->g = (colorchannel8_t)lua_tonumber(L, 3); + return 0; } else if(stringCompare(key, "b") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - color->b = (colorchannel8_t)inValue->value.intValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - color->b = (colorchannel8_t)(inValue->value.floatValue); - } else { - assertUnreachable("Invalid type for color channel b"); + if(!lua_isnumber(L, 3)) { + return luaL_error(L, "color channel b must be a number."); } - return; + color->b = (colorchannel8_t)lua_tonumber(L, 3); + return 0; } else if(stringCompare(key, "a") == 0) { - if(inValue->type == SCRIPT_VALUE_TYPE_INT) { - color->a = (colorchannel8_t)inValue->value.intValue; - } else if(inValue->type == SCRIPT_VALUE_TYPE_FLOAT) { - color->a = (colorchannel8_t)(inValue->value.floatValue); - } else { - assertUnreachable("Invalid type for color channel a"); + if(!lua_isnumber(L, 3)) { + return luaL_error(L, "color channel a must be a number."); } - return; + color->a = (colorchannel8_t)lua_tonumber(L, 3); + return 0; } + + luaL_error(L, "Invalid color channel key."); + return 0; } \ No newline at end of file diff --git a/src/script/module/display/modulecolor.h b/src/script/module/display/modulecolor.h index 5afbb28..aa3b8eb 100644 --- a/src/script/module/display/modulecolor.h +++ b/src/script/module/display/modulecolor.h @@ -24,31 +24,16 @@ void moduleColor(scriptcontext_t *context); int moduleColorFuncColor(lua_State *L); /** - * Getter function for the color structure. - * - * @param context The script context. - * @param key The key to get. - * @param structPtr Pointer to the color structure. - * @param outValue Output value. + * Index function for the color structure. + * @param L The Lua state. + * @return Number of return values. */ -void moduleColorGetter( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -); +int moduleColorIndex(lua_State *L); /** - * Setter function for the color structure. + * New index function for the color structure. * - * @param context The script context. - * @param key The key to set. - * @param structPtr Pointer to the color structure. - * @param inValue Input value. + * @param L The Lua state. + * @return Number of return values. */ -void moduleColorSetter( - const scriptcontext_t *context, - const char_t *key, - void *structPtr, - const scriptvalue_t *inValue -); \ No newline at end of file +int moduleColorNewIndex(lua_State *L); \ No newline at end of file diff --git a/src/script/module/display/moduleglm.c b/src/script/module/display/moduleglm.c index 1eaac81..925d226 100644 --- a/src/script/module/display/moduleglm.c +++ b/src/script/module/display/moduleglm.c @@ -7,46 +7,45 @@ #include "moduleglm.h" #include "assert/assert.h" -#include "script/struct/scriptstruct.h" #include "util/string.h" #include "util/memory.h" void moduleGLM(scriptcontext_t *context) { assertNotNull(context, "Context cannot be NULL."); - scriptStructRegister( - context, - "vec3_mt", - moduleGLMVec3Getter, - NULL - ); + // scriptStructRegister( + // context, + // "vec3_mt", + // moduleGLMVec3Getter, + // NULL + // ); } -void moduleGLMVec3Getter( - const scriptcontext_t *context, - 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."); +// void moduleGLMVec3Getter( +// const scriptcontext_t *context, +// 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."); - printf("Getting vec3 field %s\n", key); +// printf("Getting vec3 field %s\n", key); - vec3 *v = (vec3 *)structPtr; - if(stringCompare(key, "x") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = (*v)[0]; - return; - } else if(stringCompare(key, "y") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = (*v)[1]; - return; - } else if(stringCompare(key, "z") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = (*v)[2]; - return; - } -} \ No newline at end of file +// vec3 *v = (vec3 *)structPtr; +// if(stringCompare(key, "x") == 0) { +// outValue->type = SCRIPT_VALUE_TYPE_FLOAT; +// outValue->value.floatValue = (*v)[0]; +// return; +// } else if(stringCompare(key, "y") == 0) { +// outValue->type = SCRIPT_VALUE_TYPE_FLOAT; +// outValue->value.floatValue = (*v)[1]; +// return; +// } else if(stringCompare(key, "z") == 0) { +// outValue->type = SCRIPT_VALUE_TYPE_FLOAT; +// outValue->value.floatValue = (*v)[2]; +// return; +// } +// } \ No newline at end of file diff --git a/src/script/module/display/modulespritebatch.c b/src/script/module/display/modulespritebatch.c index ece0652..c123e98 100644 --- a/src/script/module/display/modulespritebatch.c +++ b/src/script/module/display/modulespritebatch.c @@ -10,9 +10,9 @@ #include "assert/assert.h" void moduleSpriteBatch(scriptcontext_t *context) { - scriptContextRegFunc(context, "spriteBatchFlush", moduleSpriteBatchFlush); - scriptContextRegFunc(context, "spriteBatchClear", moduleSpriteBatchClear); - scriptContextRegFunc(context, "spriteBatchPush", moduleSpriteBatchPush); + lua_register(context->luaState, "spriteBatchFlush", moduleSpriteBatchFlush); + lua_register(context->luaState, "spriteBatchClear", moduleSpriteBatchClear); + lua_register(context->luaState, "spriteBatchPush", moduleSpriteBatchPush); } int moduleSpriteBatchFlush(lua_State *L) { @@ -47,11 +47,10 @@ int moduleSpriteBatchPush(lua_State *L) { // Color (struct) or nil for white color_t *color = NULL; - if(lua_isuserdata(L, 6)) { - color = *(color_t**)lua_touserdata(L, 6); - assertNotNull(color, "Color struct cannot be NULL"); - } else if(lua_isnil(L, 6)) { - // Extrapolate later. + if(lua_gettop(L) < 6 || lua_isnil(L, 6)) { + // Allow NULL + } else if(lua_isuserdata(L, 6)) { + color = (color_t*)luaL_checkudata(L, 6, "color_mt"); } else { return luaL_error(L, "Sprite color must be a color struct or nil"); } diff --git a/src/script/module/event/moduleevent.c b/src/script/module/event/moduleevent.c index 371a129..2a8ab6b 100644 --- a/src/script/module/event/moduleevent.c +++ b/src/script/module/event/moduleevent.c @@ -41,5 +41,5 @@ int moduleEventSubscribe(lua_State *L) { void moduleEvent(scriptcontext_t *context) { // Reg functions - scriptContextRegFunc(context, "eventSubscribe", moduleEventSubscribe); + lua_register(context->luaState, "eventSubscribe", moduleEventSubscribe); } \ No newline at end of file diff --git a/src/script/module/input/moduleinput.c b/src/script/module/input/moduleinput.c index 34f13bd..a63eb8d 100644 --- a/src/script/module/input/moduleinput.c +++ b/src/script/module/input/moduleinput.c @@ -9,7 +9,6 @@ #include "input/input.h" #include "assert/assert.h" #include "util/string.h" -#include "script/struct/scriptstruct.h" void moduleInput(scriptcontext_t *context) { assertNotNull(context, "Script context cannot be NULL"); @@ -37,27 +36,39 @@ void moduleInput(scriptcontext_t *context) { ); // 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 - scriptContextRegPointer(context,"INPUT_EVENT_PRESSED",&INPUT.eventPressed); - scriptContextRegPointer(context,"INPUT_EVENT_RELEASED",&INPUT.eventReleased); + lua_pushlightuserdata(context->luaState, &INPUT.eventPressed); + lua_setglobal(context->luaState, "INPUT_EVENT_PRESSED"); + + lua_pushlightuserdata(context->luaState, &INPUT.eventReleased); + lua_setglobal(context->luaState, "INPUT_EVENT_RELEASED"); // Bind methods - scriptContextRegFunc(context, "inputBind", moduleInputBind); + lua_register(context->luaState, "inputBind", moduleInputBind); } -void moduleInputEventGetter( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -) { +int moduleInputIndex(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL"); + + const char_t *key = luaL_checkstring(l, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); + if(stringCompare(key, "action") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_INT; - outValue->value.intValue = ((const inputevent_t*)structPtr)->action; - return; + lua_pushinteger( + l, + ((const inputevent_t*)lua_touserdata(l, 1))->action + ); + return 1; } + + return 0; } int moduleInputBind(lua_State *L) { diff --git a/src/script/module/input/moduleinput.h b/src/script/module/input/moduleinput.h index 9547c16..9c473e0 100644 --- a/src/script/module/input/moduleinput.h +++ b/src/script/module/input/moduleinput.h @@ -16,19 +16,11 @@ void moduleInput(scriptcontext_t *context); /** - * Script event getter for input events. + * Getter for input structure fields. * - * @param context The script context. - * @param key The key to get. - * @param structPtr Pointer to the input event struct. - * @param outValue Output script value. + * @param l The Lua state. */ -void moduleInputEventGetter( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -); +int moduleInputIndex(lua_State *l); /** * Script binding for binding an input button to an action. diff --git a/src/script/module/item/moduleitem.c b/src/script/module/item/moduleitem.c index 8d94441..a38b9e0 100644 --- a/src/script/module/item/moduleitem.c +++ b/src/script/module/item/moduleitem.c @@ -17,19 +17,20 @@ void moduleItem(scriptcontext_t *context) { scriptContextExec(context, ITEM_SCRIPT); // Bind BACKPACK const pointer - scriptContextRegPointer(context, "BACKPACK", (void *)&BACKPACK); + lua_pushlightuserdata(context->luaState, &BACKPACK); + lua_setglobal(context->luaState, "BACKPACK"); // Bind Methods - scriptContextRegFunc( - context, "inventoryItemExists", moduleInventoryItemExists + lua_register( + context->luaState, "inventoryItemExists", moduleInventoryItemExists ); - scriptContextRegFunc(context, "inventoryAdd", moduleInventoryAdd); - scriptContextRegFunc(context, "inventorySet", moduleInventorySet); - scriptContextRegFunc(context, "inventoryRemove", moduleInventoryRemove); - scriptContextRegFunc(context, "inventoryGetCount", moduleInventoryGetCount); - scriptContextRegFunc(context, "inventoryIsFull", moduleInventoryIsFull); - scriptContextRegFunc(context, "inventoryItemFull", moduleInventoryItemFull); - scriptContextRegFunc(context, "inventorySort", moduleInventorySort); + lua_register(context->luaState, "inventoryAdd", moduleInventoryAdd); + lua_register(context->luaState, "inventorySet", moduleInventorySet); + lua_register(context->luaState, "inventoryRemove", moduleInventoryRemove); + lua_register(context->luaState, "inventoryGetCount", moduleInventoryGetCount); + lua_register(context->luaState, "inventoryIsFull", moduleInventoryIsFull); + lua_register(context->luaState, "inventoryItemFull", moduleInventoryItemFull); + lua_register(context->luaState, "inventorySort", moduleInventorySort); } int moduleInventoryItemExists(lua_State *L) { diff --git a/src/script/module/locale/modulelocale.c b/src/script/module/locale/modulelocale.c index 4f21668..e179809 100644 --- a/src/script/module/locale/modulelocale.c +++ b/src/script/module/locale/modulelocale.c @@ -15,9 +15,9 @@ void moduleLocale(scriptcontext_t *context) { // Execute the locale script definitions. scriptContextExec(context, LOCALE_SCRIPT); - scriptContextRegFunc(context, "localeGet", moduleLocaleGet); - scriptContextRegFunc(context, "localeSet", moduleLocaleSet); - scriptContextRegFunc(context, "localeGetName", moduleLocaleGetName); + lua_register(context->luaState, "localeGet", moduleLocaleGet); + lua_register(context->luaState, "localeSet", moduleLocaleSet); + lua_register(context->luaState, "localeGetName", moduleLocaleGetName); } int moduleLocaleGet(lua_State *L) { diff --git a/src/script/module/scene/modulescene.c b/src/script/module/scene/modulescene.c index d3f38bd..94b425d 100644 --- a/src/script/module/scene/modulescene.c +++ b/src/script/module/scene/modulescene.c @@ -12,7 +12,7 @@ void moduleScene(scriptcontext_t *ctx) { assertNotNull(ctx, "Script context cannot be NULL"); - scriptContextRegFunc(ctx, "sceneSet", moduleSceneSet); + lua_register(ctx->luaState, "sceneSet", moduleSceneSet); } int moduleSceneSet(lua_State *L) { diff --git a/src/script/module/system/modulesystem.c b/src/script/module/system/modulesystem.c index efe2a00..abad008 100644 --- a/src/script/module/system/modulesystem.c +++ b/src/script/module/system/modulesystem.c @@ -14,9 +14,9 @@ void moduleSystem(scriptcontext_t *context) { assertNotNull(context, "Script context cannot be NULL"); - scriptContextRegFunc(context, "print", moduleSysPrint); - scriptContextRegFunc(context, "include", moduleSysInclude); - scriptContextRegFunc(context, "module", moduleSysModule); + lua_register(context->luaState, "print", moduleSysPrint); + lua_register(context->luaState, "include", moduleSysInclude); + lua_register(context->luaState, "module", moduleSysModule); } int moduleSysPrint(lua_State *L) { diff --git a/src/script/module/time/moduletime.c b/src/script/module/time/moduletime.c index d8b8efc..6217e4f 100644 --- a/src/script/module/time/moduletime.c +++ b/src/script/module/time/moduletime.c @@ -7,31 +7,30 @@ #include "moduletime.h" #include "assert/assert.h" -#include "script/struct/scriptstruct.h" void moduleTime(scriptcontext_t *ctx) { assertNotNull(ctx, "Script context cannot be NULL"); // Script structure - scriptStructRegister(ctx, "time_mt", moduleTimeGetter, NULL); - - // Register struct - scriptStructPush(ctx, "time_mt", "TIME", &TIME); + if(luaL_newmetatable(ctx->luaState, "time_mt")) { + // Metatable is new, set __index + lua_pushstring(ctx->luaState, "__index"); + lua_pushcfunction(ctx->luaState, moduleTimeIndex); + lua_settable(ctx->luaState, -3); + } } -void moduleTimeGetter( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -) { +int moduleTimeIndex(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + + const char_t *key = lua_tostring(L, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); + if(stringCompare(key, "delta") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = TIME.delta; - return; + lua_pushnumber(L, TIME.delta); + return 1; } else if(stringCompare(key, "time") == 0) { - outValue->type = SCRIPT_VALUE_TYPE_FLOAT; - outValue->value.floatValue = TIME.time; - return; + lua_pushnumber(L, TIME.time); + return 1; } } \ No newline at end of file diff --git a/src/script/module/time/moduletime.h b/src/script/module/time/moduletime.h index 360aa51..d6b3070 100644 --- a/src/script/module/time/moduletime.h +++ b/src/script/module/time/moduletime.h @@ -18,16 +18,8 @@ void moduleTime(scriptcontext_t *ctx); /** - * Getter function for the TIME struct in scripts. - * - * @param context The script context. - * @param key The key being accessed. - * @param structPtr Pointer to the struct instance. - * @param outValue Output parameter to store the retrieved value. + * Index function for the time structure. + * @param L The Lua state. + * @return Number of return values. */ -void moduleTimeGetter( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -); \ No newline at end of file +int moduleTimeIndex(lua_State *L); \ No newline at end of file diff --git a/src/script/module/ui/moduleui.c b/src/script/module/ui/moduleui.c index 3e2b745..3de230c 100644 --- a/src/script/module/ui/moduleui.c +++ b/src/script/module/ui/moduleui.c @@ -7,9 +7,30 @@ #include "moduleui.h" #include "assert/assert.h" +#include "ui/ui.h" void moduleUi(scriptcontext_t *ctx) { 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; } \ No newline at end of file diff --git a/src/script/module/ui/moduleui.h b/src/script/module/ui/moduleui.h index 7e93ad1..dbdf789 100644 --- a/src/script/module/ui/moduleui.h +++ b/src/script/module/ui/moduleui.h @@ -13,4 +13,14 @@ * * @param ctx The script context to register the module in. */ -void moduleUi(scriptcontext_t *ctx); \ No newline at end of file +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); \ No newline at end of file diff --git a/src/script/scriptcontext.c b/src/script/scriptcontext.c index 673d5c1..b0de54f 100644 --- a/src/script/scriptcontext.c +++ b/src/script/scriptcontext.c @@ -38,31 +38,6 @@ errorret_t scriptContextInit(scriptcontext_t *context) { 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( scriptcontext_t *context, const char_t *fnName, diff --git a/src/script/scriptcontext.h b/src/script/scriptcontext.h index d043ae0..26945d3 100644 --- a/src/script/scriptcontext.h +++ b/src/script/scriptcontext.h @@ -29,32 +29,6 @@ typedef struct scriptcontext_s { */ 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. * diff --git a/src/script/struct/scriptstruct.c b/src/script/struct/scriptstruct.c deleted file mode 100644 index 2900de9..0000000 --- a/src/script/struct/scriptstruct.c +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/src/script/struct/scriptstruct.h b/src/script/struct/scriptstruct.h deleted file mode 100644 index 7d0200b..0000000 --- a/src/script/struct/scriptstruct.h +++ /dev/null @@ -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 -); \ No newline at end of file diff --git a/src/script/struct/scriptstructgetter.h b/src/script/struct/scriptstructgetter.h deleted file mode 100644 index b70190e..0000000 --- a/src/script/struct/scriptstructgetter.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -typedef void (*scriptstructgetter_t)( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -); \ No newline at end of file diff --git a/src/script/struct/scriptstructsetter.h b/src/script/struct/scriptstructsetter.h deleted file mode 100644 index 852ee19..0000000 --- a/src/script/struct/scriptstructsetter.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptcontext.h" - -typedef void (*scriptstructsetter_t)( - const scriptcontext_t *context, - const char_t *key, - void *structPtr, - const scriptvalue_t *inValue -); \ No newline at end of file diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 0155c3b..e5fe231 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2025 Dominic Masters -# +# Copyright (c) 2026 Dominic Masters +# # This software is released under the MIT License. # https://opensource.org/licenses/MIT @@ -7,11 +7,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC ui.c - uitext.c - uidebug.c - uiframe.c - uitextbox.c -) - -# Subdirs -add_subdirectory(element) \ No newline at end of file + uielement.c +) \ No newline at end of file diff --git a/src/ui/ui.c b/src/ui/ui.c index c6bf883..16a8c22 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -1,67 +1,68 @@ /** - * Copyright (c) 2025 Dominic Masters + * Copyright (c) 2026 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" +#include "assert/assert.h" +#include "display/spritebatch.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(); + uint_fast8_t i = 0; + while(i < UI.stackSize) { + errorCatch(errorPrint(uiElementUpdate(&UI.stack[i]))); + i++; + } } void uiRender(void) { cameraPushMatrix(&UI.camera); - - // Render UI elements here - if(UI.fontTexture.width > 0) { - uiDebugRender(UI.fontTileset, &UI.fontTexture); + spriteBatchClear(); + + uint_fast8_t i = 0; + 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(); } -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)); +errorret_t uiPushScript(const char_t *path) { + assertTrue(UI.stackSize < UI_STACK_SIZE, "UI stack overflow"); + uielement_t *element = &UI.stack[UI.stackSize]; + errorChain(uiElementInitScript(element, path)); + UI.stackSize++; + // Ret Id or something? 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) { - if(UI.fontTexture.width > 0) { - textureDispose(&UI.fontTexture); - UI.fontTexture.width = -1; + while(UI.stackSize > 0) { + uiPop(); } } \ No newline at end of file diff --git a/src/ui/ui.h b/src/ui/ui.h index 956fe45..70da817 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -1,51 +1,53 @@ /** - * Copyright (c) 2025 Dominic Masters + * Copyright (c) 2026 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" +#include "ui/uielement.h" + +#define UI_STACK_SIZE 64 typedef struct { camera_t camera; - texture_t fontTexture; - const tileset_t *fontTileset; + + uielement_t stack[UI_STACK_SIZE]; + uint_fast8_t stackSize; } ui_t; extern ui_t UI; /** - * Initializes the UI system, loading necessary resources. - * - * @return An errorret_t indicating success or failure. + * Initializes the UI system. */ errorret_t uiInit(void); /** - * Updates the UI state, handling user interactions and animations. + * Updates the UI system. */ void uiUpdate(void); /** - * Renders the UI elements to the screen. + * Renders the UI system. */ 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. - * - * @return An errorret_t indicating success or failure. + * @param path Path to the UI script file. */ -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); \ No newline at end of file diff --git a/src/ui/uielement.c b/src/ui/uielement.c new file mode 100644 index 0000000..afa65b2 --- /dev/null +++ b/src/ui/uielement.c @@ -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); +} \ No newline at end of file diff --git a/src/ui/uielement.h b/src/ui/uielement.h new file mode 100644 index 0000000..1e2f928 --- /dev/null +++ b/src/ui/uielement.h @@ -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); \ No newline at end of file