From 5cea2849062e8eae49882ebf41c6ad661009df32 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 3 Feb 2026 19:20:50 -0600 Subject: [PATCH] Map loading --- archive/rpg/overworld/map.c | 2 +- assets/init.lua | 3 +- assets/map/testmap/CMakeLists.txt | 3 +- assets/map/testmap/testmap.lua | 1 - assets/scene/initial.lua | 21 ++- src/asset/assettype.h | 16 ++ src/asset/type/CMakeLists.txt | 2 + {archive => src/asset/type}/assetmap.c | 8 +- {archive => src/asset/type}/assetmap.h | 6 +- .../asset/type/assetmapchunk.c | 64 ++++---- .../asset/type/assetmapchunk.h | 5 +- src/map/map.c | 28 ++-- src/map/map.h | 5 + src/map/mapchunk.c | 6 + src/map/mapchunk.h | 9 +- src/script/module/CMakeLists.txt | 1 + src/script/module/display/modulecamera.c | 39 ++++- src/script/module/display/moduleglm.c | 147 ++++++++++++++---- src/script/module/display/moduleglm.h | 39 +++-- src/script/module/display/moduletext.c | 6 +- src/script/module/map/CMakeLists.txt | 10 ++ src/script/module/map/modulemap.c | 60 +++++++ src/script/module/map/modulemap.h | 48 ++++++ src/script/scriptmodule.c | 2 + tools/asset/process/map.py | 4 +- 25 files changed, 415 insertions(+), 120 deletions(-) delete mode 100644 assets/map/testmap/testmap.lua rename {archive => src/asset/type}/assetmap.c (55%) rename {archive => src/asset/type}/assetmap.h (78%) rename archive/assetchunk.c => src/asset/type/assetmapchunk.c (74%) rename archive/assetchunk.h => src/asset/type/assetmapchunk.h (69%) create mode 100644 src/script/module/map/CMakeLists.txt create mode 100644 src/script/module/map/modulemap.c create mode 100644 src/script/module/map/modulemap.h diff --git a/archive/rpg/overworld/map.c b/archive/rpg/overworld/map.c index ff9123c..32ff403 100644 --- a/archive/rpg/overworld/map.c +++ b/archive/rpg/overworld/map.c @@ -209,7 +209,7 @@ errorret_t mapChunkLoad(chunk_t* chunk) { memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); // Get chunk filepath. - snprintf(buffer, sizeof(buffer), "%s/chunks/%d_%d_%d.dcf", + snprintf(buffer, sizeof(buffer), "%s/chunks/%d_%d_%d.dmc", MAP.dirPath, chunk->position.x, chunk->position.y, diff --git a/assets/init.lua b/assets/init.lua index 7fc695b..46a3d5e 100644 --- a/assets/init.lua +++ b/assets/init.lua @@ -38,5 +38,4 @@ else end localeSet(DUSK_LOCALE_EN_US) -sceneSet('scene/initial.dsf') --- mapLoad('map/testmap/testmap.dmf') \ No newline at end of file +sceneSet('scene/initial.dsf') \ No newline at end of file diff --git a/assets/map/testmap/CMakeLists.txt b/assets/map/testmap/CMakeLists.txt index 61ddf2b..9874942 100644 --- a/assets/map/testmap/CMakeLists.txt +++ b/assets/map/testmap/CMakeLists.txt @@ -3,5 +3,4 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -add_asset(MAP testmap.json) -add_asset(SCRIPT testmap.lua) \ No newline at end of file +add_asset(MAP testmap.json) \ No newline at end of file diff --git a/assets/map/testmap/testmap.lua b/assets/map/testmap/testmap.lua deleted file mode 100644 index 7583401..0000000 --- a/assets/map/testmap/testmap.lua +++ /dev/null @@ -1 +0,0 @@ -print('Test Map Script Run') \ No newline at end of file diff --git a/assets/scene/initial.lua b/assets/scene/initial.lua index 8a03296..48e1b66 100644 --- a/assets/scene/initial.lua +++ b/assets/scene/initial.lua @@ -5,10 +5,15 @@ module('color') module('text') module('screen') module('time') +module('map') +module('glm') -camera = cameraCreate(CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) -text = "Hello World" screenSetBackground(colorBlack()) +mapLoad('map/testmap/testmap.dmf') +camera = cameraCreate(CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) +mapCamera = cameraCreate() + +text = "Hello World" function sceneDispose() end @@ -17,19 +22,23 @@ function sceneUpdate() end function sceneRender() - cameraPushMatrix(camera) + -- Map Test + cameraPushMatrix(mapCamera) + mapCamera.position = vec3(300, 300, 300) + mapRender() + cameraPopMatrix() + -- UI Test + cameraPushMatrix(camera) camera.bottom = screenGetHeight() camera.right = screenGetWidth() width, height = textMeasure(text) x = (screenGetWidth() - width) x = math.sin(TIME.time * 2) * (x / 2) + (x / 2) - y = (screenGetHeight() - height) / 2 y = math.cos(TIME.time * 3) * (y) + (y) - - textDraw(x, y, text) + textDraw(x, y, text, colorMagenta()) cameraPopMatrix() end \ No newline at end of file diff --git a/src/asset/assettype.h b/src/asset/assettype.h index 90b392c..c7cbcc9 100644 --- a/src/asset/assettype.h +++ b/src/asset/assettype.h @@ -10,6 +10,8 @@ #include "type/assetalphaimage.h" #include "type/assetlanguage.h" #include "type/assetscript.h" +#include "type/assetmap.h" +#include "type/assetmapchunk.h" #include typedef enum { @@ -19,6 +21,8 @@ typedef enum { ASSET_TYPE_ALPHA_IMAGE, ASSET_TYPE_LANGUAGE, ASSET_TYPE_SCRIPT, + ASSET_TYPE_MAP, + ASSET_TYPE_MAP_CHUNK, ASSET_TYPE_COUNT, } assettype_t; @@ -73,4 +77,16 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = { .loadStrategy = ASSET_LOAD_STRAT_CUSTOM, .custom = assetScriptHandler }, + + [ASSET_TYPE_MAP] = { + .header = "DMF", + .loadStrategy = ASSET_LOAD_STRAT_CUSTOM, + .custom = assetMapHandler + }, + + [ASSET_TYPE_MAP_CHUNK] = { + .header = "DMC", + .loadStrategy = ASSET_LOAD_STRAT_CUSTOM, + .custom = assetMapChunkHandler + }, }; \ No newline at end of file diff --git a/src/asset/type/CMakeLists.txt b/src/asset/type/CMakeLists.txt index 1a60a2f..32eb64a 100644 --- a/src/asset/type/CMakeLists.txt +++ b/src/asset/type/CMakeLists.txt @@ -10,4 +10,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} assetpaletteimage.c assetlanguage.c assetscript.c + assetmap.c + assetmapchunk.c ) \ No newline at end of file diff --git a/archive/assetmap.c b/src/asset/type/assetmap.c similarity index 55% rename from archive/assetmap.c rename to src/asset/type/assetmap.c index 875db74..03460cf 100644 --- a/archive/assetmap.c +++ b/src/asset/type/assetmap.c @@ -9,11 +9,7 @@ #include "assert/assert.h" #include "util/memory.h" -errorret_t assetMapLoad(void *data, void *output) { - assertNotNull(data, "Data cannot be NULL"); - assertNotNull(output, "Output cannot be NULL"); - - assertUnreachable("map not finished"); - +errorret_t assetMapHandler(assetcustom_t custom) { + printf("Map Loaded from asset!\n"); errorOk(); } \ No newline at end of file diff --git a/archive/assetmap.h b/src/asset/type/assetmap.h similarity index 78% rename from archive/assetmap.h rename to src/asset/type/assetmap.h index f3a6262..772c32d 100644 --- a/archive/assetmap.h +++ b/src/asset/type/assetmap.h @@ -7,8 +7,8 @@ #pragma once #include "error/error.h" -#include "rpg/overworld/map.h" -#include "display/mesh/mesh.h" + +typedef struct assetcustom_s assetcustom_t; /** * Loads a map asset from the given data pointer into the output map structure. @@ -17,4 +17,4 @@ * @param output Pointer to the map_t to load the map into. * @return An error code. */ -errorret_t assetMapLoad(void *data, void *output); \ No newline at end of file +errorret_t assetMapHandler(assetcustom_t custom); \ No newline at end of file diff --git a/archive/assetchunk.c b/src/asset/type/assetmapchunk.c similarity index 74% rename from archive/assetchunk.c rename to src/asset/type/assetmapchunk.c index d4e8063..2169291 100644 --- a/archive/assetchunk.c +++ b/src/asset/type/assetmapchunk.c @@ -7,7 +7,7 @@ #include "asset/asset.h" #include "assert/assert.h" -#include "rpg/entity/entity.h" +#include "map/mapchunk.h" #pragma pack(push, 1) typedef struct { @@ -19,7 +19,7 @@ typedef struct { #pragma pack(push, 1) typedef struct { - tile_t tile; + maptile_t tile; } assetchunktiledata_t; #pragma pack(pop) @@ -31,18 +31,18 @@ typedef struct { #pragma pack(push, 1) typedef struct { - entitytype_t entityType; + uint8_t entityType; uint8_t localX; uint8_t localY; uint8_t localZ; } assetchunkentityheader_t; #pragma pack(pop) -errorret_t assetChunkLoad(assetcustom_t custom) { +errorret_t assetMapChunkHandler(assetcustom_t custom) { assertNotNull(custom.output, "Output pointer cannot be NULL"); assertNotNull(custom.zipFile, "Zip file pointer cannot be NULL"); - chunk_t *chunk = (chunk_t *)custom.output; + mapchunk_t *chunk = (mapchunk_t *)custom.output; assertTrue(chunk->meshCount == 0, "Chunk is not in a good state"); // Read header @@ -143,36 +143,36 @@ errorret_t assetChunkLoad(assetcustom_t custom) { } // Read entity data - for(uint8_t i = 0; i < header.entityCount; i++) { - assetchunkentityheader_t entityHeader; - bytesRead = zip_fread( - custom.zipFile, &entityHeader, sizeof(assetchunkentityheader_t) - ); - if(bytesRead != sizeof(assetchunkentityheader_t)) { - zip_fclose(custom.zipFile); - errorThrow("Failed to read chunk entity header."); - } + // for(uint8_t i = 0; i < header.entityCount; i++) { + // assetchunkentityheader_t entityHeader; + // bytesRead = zip_fread( + // custom.zipFile, &entityHeader, sizeof(assetchunkentityheader_t) + // ); + // if(bytesRead != sizeof(assetchunkentityheader_t)) { + // zip_fclose(custom.zipFile); + // errorThrow("Failed to read chunk entity header."); + // } - uint8_t entityIndex = entityGetAvailable(); - if(entityIndex == 0xFF) { - zip_fclose(custom.zipFile); - errorThrow("No available entity slots."); - } + // uint8_t entityIndex = entityGetAvailable(); + // if(entityIndex == 0xFF) { + // zip_fclose(custom.zipFile); + // errorThrow("No available entity slots."); + // } - entity_t *entity = &ENTITIES[entityIndex]; - entityInit(entity, (entitytype_t)entityHeader.entityType); - entity->position.x = ( - (chunk->position.x * CHUNK_WIDTH) + entityHeader.localX - ); - entity->position.y = ( - (chunk->position.y * CHUNK_HEIGHT) + entityHeader.localY - ); - entity->position.z = ( - (chunk->position.z * CHUNK_DEPTH) + entityHeader.localZ - ); + // entity_t *entity = &ENTITIES[entityIndex]; + // entityInit(entity, (entitytype_t)entityHeader.entityType); + // entity->position.x = ( + // (chunk->position.x * CHUNK_WIDTH) + entityHeader.localX + // ); + // entity->position.y = ( + // (chunk->position.y * CHUNK_HEIGHT) + entityHeader.localY + // ); + // entity->position.z = ( + // (chunk->position.z * CHUNK_DEPTH) + entityHeader.localZ + // ); - chunk->entities[i] = entityIndex; - } + // chunk->entities[i] = entityIndex; + // } errorOk(); } \ No newline at end of file diff --git a/archive/assetchunk.h b/src/asset/type/assetmapchunk.h similarity index 69% rename from archive/assetchunk.h rename to src/asset/type/assetmapchunk.h index a474bda..f80e47b 100644 --- a/archive/assetchunk.h +++ b/src/asset/type/assetmapchunk.h @@ -7,14 +7,13 @@ #pragma once #include "error/error.h" -#include "rpg/overworld/chunk.h" typedef struct assetcustom_s assetcustom_t; /** - * Handles loading of chunk data from a chunk asset file. + * Handles loading of map chunk data from a map chunk asset file. * * @param custom The custom asset loading parameters. * @return An error code. */ -errorret_t assetChunkLoad(assetcustom_t custom); \ No newline at end of file +errorret_t assetMapChunkHandler(assetcustom_t custom); \ No newline at end of file diff --git a/src/map/map.c b/src/map/map.c index 3769e91..21e1d0e 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -30,6 +30,7 @@ errorret_t mapLoad(const char_t *path, const chunkpos_t position) { if(stringCompare(MAP.filePath, path) == 0) { // Same map, no need to reload + errorChain(mapPositionSet(position)); errorOk(); } @@ -58,6 +59,9 @@ errorret_t mapLoad(const char_t *path, const chunkpos_t position) { if(last == NULL) errorThrow("Map file name has no extension"); *last = '\0'; // Terminate to remove extension + // Load map itself + errorChain(assetLoad(MAP.filePath, &MAP)); + // Reset map position MAP.chunkPosition = position; @@ -77,19 +81,6 @@ errorret_t mapLoad(const char_t *path, const chunkpos_t position) { } } - // Execute map script. - // char_t scriptPath[MAP_FILE_PATH_MAX + 16]; - // stringFormat( - // scriptPath, sizeof(scriptPath), "%s/%s.dsf", - // MAP.dirPath, MAP.fileName - // ); - // if(assetFileExists(scriptPath)) { - // scriptcontext_t ctx; - // errorChain(scriptContextInit(&ctx)); - // errorChain(scriptContextExecFile(&ctx, scriptPath)); - // scriptContextDispose(&ctx); - // } - errorOk(); } @@ -181,6 +172,15 @@ void mapUpdate() { #endif } +void mapRender() { + if(!mapIsLoaded()) return; + + textureBind(NULL); + for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { + mapChunkRender(&MAP.chunks[i]); + } +} + void mapDispose() { for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { mapChunkUnload(&MAP.chunks[i]); @@ -211,7 +211,7 @@ errorret_t mapChunkLoad(mapchunk_t* chunk) { memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); // Get chunk filepath. - snprintf(buffer, sizeof(buffer), "%s/chunks/%d_%d_%d.dcf", + snprintf(buffer, sizeof(buffer), "%s/chunks/%d_%d_%d.dmc", MAP.dirPath, chunk->position.x, chunk->position.y, diff --git a/src/map/map.h b/src/map/map.h index 339c364..24a615a 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -50,6 +50,11 @@ errorret_t mapLoad(const char_t *path, const chunkpos_t position); */ void mapUpdate(); +/** + * Renders the map. + */ +void mapRender(); + /** * Disposes of the map. */ diff --git a/src/map/mapchunk.c b/src/map/mapchunk.c index 20f004b..5af06ab 100644 --- a/src/map/mapchunk.c +++ b/src/map/mapchunk.c @@ -17,4 +17,10 @@ uint32_t mapChunkGetTileindex(const chunkpos_t position) { bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b) { return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); +} + +void mapChunkRender(const mapchunk_t *chunk) { + for(uint8_t i = 0; i < chunk->meshCount; i++) { + meshDraw(&chunk->meshes[i], 0, -1); + } } \ No newline at end of file diff --git a/src/map/mapchunk.h b/src/map/mapchunk.h index 0fd53b1..c65304d 100644 --- a/src/map/mapchunk.h +++ b/src/map/mapchunk.h @@ -35,4 +35,11 @@ uint32_t mapChunkGetTileindex(const chunkpos_t position); * @param b The second chunk position. * @return true if equal, false otherwise. */ -bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b); \ No newline at end of file +bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b); + +/** + * Renders the given map chunk. + * + * @param chunk The map chunk to render. + */ +void mapChunkRender(const mapchunk_t *chunk); \ No newline at end of file diff --git a/src/script/module/CMakeLists.txt b/src/script/module/CMakeLists.txt index 46bec9c..f3086ae 100644 --- a/src/script/module/CMakeLists.txt +++ b/src/script/module/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(event) add_subdirectory(input) add_subdirectory(item) add_subdirectory(locale) +add_subdirectory(map) add_subdirectory(system) add_subdirectory(scene) add_subdirectory(story) diff --git a/src/script/module/display/modulecamera.c b/src/script/module/display/modulecamera.c index 7bf0748..ec73e55 100644 --- a/src/script/module/display/modulecamera.c +++ b/src/script/module/display/modulecamera.c @@ -185,7 +185,25 @@ int moduleCameraIndex(lua_State *l) { if(cam->viewType == CAMERA_VIEW_TYPE_MATRIX) { } else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) { - // TODO: Push vec3 + if(stringCompare(key, "position") == 0) { + vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3)); + memoryCopy(v, &cam->lookat.position, sizeof(vec3)); + luaL_getmetatable(l, "vec3_mt"); + lua_setmetatable(l, -2); + return 1; + } else if(stringCompare(key, "target") == 0) { + vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3)); + memoryCopy(v, &cam->lookat.target, sizeof(vec3)); + luaL_getmetatable(l, "vec3_mt"); + lua_setmetatable(l, -2); + return 1; + } else if(stringCompare(key, "up") == 0) { + vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3)); + memoryCopy(v, &cam->lookat.up, sizeof(vec3)); + luaL_getmetatable(l, "vec3_mt"); + lua_setmetatable(l, -2); + return 1; + } } else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT) { } else if(cam->viewType == CAMERA_VIEW_TYPE_2D) { @@ -267,5 +285,24 @@ int moduleCameraNewIndex(lua_State *l) { } } + if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) { + if(stringCompare(key, "position") == 0) { + vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt"); + assertNotNull(v, "Vec3 pointer cannot be NULL."); + memoryCopy(&cam->lookat.position, v, sizeof(vec3)); + return 0; + } else if(stringCompare(key, "target") == 0) { + vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt"); + assertNotNull(v, "Vec3 pointer cannot be NULL."); + memoryCopy(&cam->lookat.target, v, sizeof(vec3)); + return 0; + } else if(stringCompare(key, "up") == 0) { + vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt"); + assertNotNull(v, "Vec3 pointer cannot be NULL."); + memoryCopy(&cam->lookat.up, v, sizeof(vec3)); + return 0; + } + } + return 0; } \ No newline at end of file diff --git a/src/script/module/display/moduleglm.c b/src/script/module/display/moduleglm.c index 925d226..14f0368 100644 --- a/src/script/module/display/moduleglm.c +++ b/src/script/module/display/moduleglm.c @@ -13,39 +13,122 @@ void moduleGLM(scriptcontext_t *context) { assertNotNull(context, "Context cannot be NULL."); - // scriptStructRegister( - // context, - // "vec3_mt", - // moduleGLMVec3Getter, - // NULL - // ); + // Create metatable for vec3 structure. + if(luaL_newmetatable(context->luaState, "vec3_mt")) { + // Metatable methods + lua_pushcfunction(context->luaState, moduleVec3Index); + lua_setfield(context->luaState, -2, "__index"); + lua_pushcfunction(context->luaState, moduleVec3NewIndex); + lua_setfield(context->luaState, -2, "__newindex"); + lua_pushcfunction(context->luaState, moduleVec3ToString); + lua_setfield(context->luaState, -2, "__tostring"); + lua_pop(context->luaState, 1); + } + + lua_register(context->luaState, "vec3", moduleVec3Create); } -// 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."); +int moduleVec3Create(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL."); -// printf("Getting vec3 field %s\n", key); + vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3)); + memoryZero(v, sizeof(vec3)); -// 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 + // May be expecting between 1 and 3 values. + int top = lua_gettop(l); + if(top >= 1) { + if(!lua_isnumber(l, 1)) { + luaL_error(l, "Vec3 x component must be a number."); + } + (*v)[0] = (float_t)lua_tonumber(l, 1); + } + if(top >= 2) { + if(!lua_isnumber(l, 2)) { + luaL_error(l, "Vec3 y component must be a number."); + } + (*v)[1] = (float_t)lua_tonumber(l, 2); + } + if(top >= 3) { + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Vec3 z component must be a number."); + } + (*v)[2] = (float_t)lua_tonumber(l, 3); + } + + // Set metatable + luaL_getmetatable(l, "vec3_mt"); + lua_setmetatable(l, -2); + + return 1; +} + +int moduleVec3Index(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL."); + + const char_t *key = lua_tostring(l, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); + + vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); + assertNotNull(vec, "Vec3 pointer cannot be NULL."); + + if(stringCompare(key, "x") == 0) { + lua_pushnumber(l, (*vec)[0]); + return 1; + } else if(stringCompare(key, "y") == 0) { + lua_pushnumber(l, (*vec)[1]); + return 1; + } else if(stringCompare(key, "z") == 0) { + lua_pushnumber(l, (*vec)[2]); + return 1; + } +} + +int moduleVec3NewIndex(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL."); + + const char_t *key = luaL_checkstring(l, 2); + assertStrLenMin(key, 1, "Key cannot be empty."); + + vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); + assertNotNull(vec, "Vec3 pointer cannot be NULL."); + + if(stringCompare(key, "x") == 0) { + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Vec3 x component must be a number."); + } + (*vec)[0] = (float_t)lua_tonumber(l, 3); + return 0; + } else if(stringCompare(key, "y") == 0) { + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Vec3 y component must be a number."); + } + (*vec)[1] = (float_t)lua_tonumber(l, 3); + return 0; + } else if(stringCompare(key, "z") == 0) { + if(!lua_isnumber(l, 3)) { + luaL_error(l, "Vec3 z component must be a number."); + } + (*vec)[2] = (float_t)lua_tonumber(l, 3); + return 0; + } + + luaL_error(l, "Invalid key for vec3: %s", key); + return 0; +} + +int moduleVec3ToString(lua_State *l) { + assertNotNull(l, "Lua state cannot be NULL."); + + vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); + assertNotNull(vec, "Vec3 pointer cannot be NULL."); + + char buf[128]; + snprintf( + buf, sizeof(buf), + "vec3(%.3f, %.3f, %.3f)", + (*vec)[0], (*vec)[1], (*vec)[2] + ); + lua_pushstring(l, buf); + + return 1; +} \ No newline at end of file diff --git a/src/script/module/display/moduleglm.h b/src/script/module/display/moduleglm.h index 26965dc..a60f990 100644 --- a/src/script/module/display/moduleglm.h +++ b/src/script/module/display/moduleglm.h @@ -16,16 +16,33 @@ void moduleGLM(scriptcontext_t *context); /** - * Getter function for the vec3 structure. + * Creates a new vec3 structure in Lua. * - * @param context The script context. - * @param key The key to get. - * @param structPtr Pointer to the vec3 structure. - * @param outValue Pointer to store the output value. + * @param l The Lua state. + * @return Number of return values on the Lua stack. */ -void moduleGLMVec3Getter( - const scriptcontext_t *context, - const char_t *key, - const void *structPtr, - scriptvalue_t *outValue -); \ No newline at end of file +int moduleVec3Create(lua_State *l); + +/** + * Lua __index metamethod for vec3 structure. + * + * @param l The Lua state. + * @return Number of return values on the Lua stack. + */ +int moduleVec3Index(lua_State *l); + +/** + * Lua __newindex metamethod for vec3 structure. + * + * @param l The Lua state. + * @return Number of return values on the Lua stack. + */ +int moduleVec3NewIndex(lua_State *l); + +/** + * Lua __tostring metamethod for vec3 structure. + * + * @param l The Lua state. + * @return Number of return values on the Lua stack. + */ +int moduleVec3ToString(lua_State *l); \ No newline at end of file diff --git a/src/script/module/display/moduletext.c b/src/script/module/display/moduletext.c index 1596dcf..8805825 100644 --- a/src/script/module/display/moduletext.c +++ b/src/script/module/display/moduletext.c @@ -39,10 +39,10 @@ int moduleTextDraw(lua_State *L) { // Optional color color_t *color = NULL; - if(lua_gettop(L) < 6 || lua_isnil(L, 6)) { + if(lua_gettop(L) < 4 || lua_isnil(L, 4)) { // Allow NULL - } else if(lua_isuserdata(L, 6)) { - color = (color_t*)luaL_checkudata(L, 6, "color_mt"); + } else if(lua_isuserdata(L, 4)) { + color = (color_t*)luaL_checkudata(L, 4, "color_mt"); } else { return luaL_error(L, "Sprite color must be a color struct or nil"); } diff --git a/src/script/module/map/CMakeLists.txt b/src/script/module/map/CMakeLists.txt new file mode 100644 index 0000000..58b1149 --- /dev/null +++ b/src/script/module/map/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + modulemap.c +) \ No newline at end of file diff --git a/src/script/module/map/modulemap.c b/src/script/module/map/modulemap.c new file mode 100644 index 0000000..08fd4d9 --- /dev/null +++ b/src/script/module/map/modulemap.c @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "modulemap.h" +#include "assert/assert.h" +#include "map/map.h" + +void moduleMap(scriptcontext_t *ctx) { + assertNotNull(ctx, "Script context cannot be NULL"); + + // Register map functions + lua_register(ctx->luaState, "mapIsLoaded", moduleMapIsLoaded); + lua_register(ctx->luaState, "mapGetTile", moduleMapGetTile); + lua_register(ctx->luaState, "mapRender", moduleMapRender); + lua_register(ctx->luaState, "mapLoad", moduleMapLoad); +} + +int moduleMapIsLoaded(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + lua_pushboolean(L, mapIsLoaded()); + return 1; +} + +int moduleMapGetTile(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + return 1; +} + +int moduleMapRender(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + mapRender(); + return 0; +} + +int moduleMapLoad(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL."); + + if(!lua_isstring(L, 1)) { + luaL_error(L, "Expected string as first argument (file path)."); + return 0; + } + const char_t *path = lua_tostring(L, 1); + + + // Optional position. + chunkpos_t position = {0, 0, 0}; + + errorret_t err = mapLoad(path, position); + if(err.code != ERROR_OK) { + errorCatch(errorPrint(err)); + luaL_error(L, "Failed to load map!"); + return 0; + } + + return 0; +} \ No newline at end of file diff --git a/src/script/module/map/modulemap.h b/src/script/module/map/modulemap.h new file mode 100644 index 0000000..ff08cce --- /dev/null +++ b/src/script/module/map/modulemap.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/scriptcontext.h" + +/** + * Register map functions to the given script context. + * + * @param context The script context to register map functions to. + */ +void moduleMap(scriptcontext_t *context); + +/** + * Script function to check if a map is loaded. + * + * @param L The Lua state. + * @return Number of return values on the Lua stack. + */ +int moduleMapIsLoaded(lua_State *L); + +/** + * Script function to get the tile at a given world position. + * + * @param L The Lua state. + * @return Number of return values on the Lua stack. + */ +int moduleMapGetTile(lua_State *L); + +/** + * Script function to render the map. + * + * @param L The Lua state. + * @return Number of return values on the Lua stack. + */ +int moduleMapRender(lua_State *L); + +/** + * Script function to load a map from a given file path. + * + * @param L The Lua state. + * @return Number of return values on the Lua stack. + */ +int moduleMapLoad(lua_State *L); \ No newline at end of file diff --git a/src/script/scriptmodule.c b/src/script/scriptmodule.c index 1e27301..74e5395 100644 --- a/src/script/scriptmodule.c +++ b/src/script/scriptmodule.c @@ -22,6 +22,7 @@ #include "script/module/display/moduletext.h" #include "script/module/display/modulescreen.h" #include "script/module/story/modulestoryflag.h" +#include "script/module/map/modulemap.h" #include "util/string.h" const scriptmodule_t SCRIPT_MODULE_LIST[] = { @@ -41,6 +42,7 @@ const scriptmodule_t SCRIPT_MODULE_LIST[] = { { .name = "text", .callback = moduleText }, { .name = "screen", .callback = moduleScreen }, { .name = "storyflag", .callback = moduleStoryFlag }, + { .name = "map", .callback = moduleMap }, }; #define SCRIPT_MODULE_COUNT ( \ diff --git a/tools/asset/process/map.py b/tools/asset/process/map.py index 35cc438..f4384e9 100644 --- a/tools/asset/process/map.py +++ b/tools/asset/process/map.py @@ -51,7 +51,7 @@ def processChunk(chunk): # Generate binary buffer for efficient output buffer = bytearray() - buffer.extend(b'DCF')# Header + buffer.extend(b'DMC')# Header buffer.extend(len(chunk.tiles).to_bytes(4, 'little')) # Number of tiles buffer.extend(len(models).to_bytes(1, 'little')) # Number of models buffer.extend(len(chunk.entities).to_bytes(1, 'little')) # Number of entities @@ -92,7 +92,7 @@ def processChunk(chunk): # Write out map file relative = getAssetRelativePath(chunk.getFilename()) fileNameWithoutExt = os.path.splitext(os.path.basename(relative))[0] - outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dcf") + outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dmc") outputFilePath = os.path.join(args.output_assets, outputFileRelative) os.makedirs(os.path.dirname(outputFilePath), exist_ok=True) with open(outputFilePath, "wb") as f: