Map loading
All checks were successful
Build Dusk / run-tests (push) Successful in 1m15s
Build Dusk / build-linux (push) Successful in 1m33s
Build Dusk / build-psp (push) Successful in 1m56s

This commit is contained in:
2026-02-03 19:20:50 -06:00
parent 13dba8b604
commit 5cea284906
25 changed files with 415 additions and 120 deletions

View File

@@ -209,7 +209,7 @@ errorret_t mapChunkLoad(chunk_t* chunk) {
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
// Get chunk filepath. // 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, MAP.dirPath,
chunk->position.x, chunk->position.x,
chunk->position.y, chunk->position.y,

View File

@@ -39,4 +39,3 @@ end
localeSet(DUSK_LOCALE_EN_US) localeSet(DUSK_LOCALE_EN_US)
sceneSet('scene/initial.dsf') sceneSet('scene/initial.dsf')
-- mapLoad('map/testmap/testmap.dmf')

View File

@@ -4,4 +4,3 @@
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_asset(MAP testmap.json) add_asset(MAP testmap.json)
add_asset(SCRIPT testmap.lua)

View File

@@ -1 +0,0 @@
print('Test Map Script Run')

View File

@@ -5,10 +5,15 @@ module('color')
module('text') module('text')
module('screen') module('screen')
module('time') module('time')
module('map')
module('glm')
camera = cameraCreate(CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC)
text = "Hello World"
screenSetBackground(colorBlack()) screenSetBackground(colorBlack())
mapLoad('map/testmap/testmap.dmf')
camera = cameraCreate(CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC)
mapCamera = cameraCreate()
text = "Hello World"
function sceneDispose() function sceneDispose()
end end
@@ -17,19 +22,23 @@ function sceneUpdate()
end end
function sceneRender() function sceneRender()
cameraPushMatrix(camera) -- Map Test
cameraPushMatrix(mapCamera)
mapCamera.position = vec3(300, 300, 300)
mapRender()
cameraPopMatrix()
-- UI Test
cameraPushMatrix(camera)
camera.bottom = screenGetHeight() camera.bottom = screenGetHeight()
camera.right = screenGetWidth() camera.right = screenGetWidth()
width, height = textMeasure(text) width, height = textMeasure(text)
x = (screenGetWidth() - width) x = (screenGetWidth() - width)
x = math.sin(TIME.time * 2) * (x / 2) + (x / 2) x = math.sin(TIME.time * 2) * (x / 2) + (x / 2)
y = (screenGetHeight() - height) / 2 y = (screenGetHeight() - height) / 2
y = math.cos(TIME.time * 3) * (y) + (y) y = math.cos(TIME.time * 3) * (y) + (y)
textDraw(x, y, text, colorMagenta())
textDraw(x, y, text)
cameraPopMatrix() cameraPopMatrix()
end end

View File

@@ -10,6 +10,8 @@
#include "type/assetalphaimage.h" #include "type/assetalphaimage.h"
#include "type/assetlanguage.h" #include "type/assetlanguage.h"
#include "type/assetscript.h" #include "type/assetscript.h"
#include "type/assetmap.h"
#include "type/assetmapchunk.h"
#include <zip.h> #include <zip.h>
typedef enum { typedef enum {
@@ -19,6 +21,8 @@ typedef enum {
ASSET_TYPE_ALPHA_IMAGE, ASSET_TYPE_ALPHA_IMAGE,
ASSET_TYPE_LANGUAGE, ASSET_TYPE_LANGUAGE,
ASSET_TYPE_SCRIPT, ASSET_TYPE_SCRIPT,
ASSET_TYPE_MAP,
ASSET_TYPE_MAP_CHUNK,
ASSET_TYPE_COUNT, ASSET_TYPE_COUNT,
} assettype_t; } assettype_t;
@@ -73,4 +77,16 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM, .loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
.custom = assetScriptHandler .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
},
}; };

View File

@@ -10,4 +10,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
assetpaletteimage.c assetpaletteimage.c
assetlanguage.c assetlanguage.c
assetscript.c assetscript.c
assetmap.c
assetmapchunk.c
) )

View File

@@ -9,11 +9,7 @@
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
errorret_t assetMapLoad(void *data, void *output) { errorret_t assetMapHandler(assetcustom_t custom) {
assertNotNull(data, "Data cannot be NULL"); printf("Map Loaded from asset!\n");
assertNotNull(output, "Output cannot be NULL");
assertUnreachable("map not finished");
errorOk(); errorOk();
} }

View File

@@ -7,8 +7,8 @@
#pragma once #pragma once
#include "error/error.h" #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. * 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. * @param output Pointer to the map_t to load the map into.
* @return An error code. * @return An error code.
*/ */
errorret_t assetMapLoad(void *data, void *output); errorret_t assetMapHandler(assetcustom_t custom);

View File

@@ -7,7 +7,7 @@
#include "asset/asset.h" #include "asset/asset.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "rpg/entity/entity.h" #include "map/mapchunk.h"
#pragma pack(push, 1) #pragma pack(push, 1)
typedef struct { typedef struct {
@@ -19,7 +19,7 @@ typedef struct {
#pragma pack(push, 1) #pragma pack(push, 1)
typedef struct { typedef struct {
tile_t tile; maptile_t tile;
} assetchunktiledata_t; } assetchunktiledata_t;
#pragma pack(pop) #pragma pack(pop)
@@ -31,18 +31,18 @@ typedef struct {
#pragma pack(push, 1) #pragma pack(push, 1)
typedef struct { typedef struct {
entitytype_t entityType; uint8_t entityType;
uint8_t localX; uint8_t localX;
uint8_t localY; uint8_t localY;
uint8_t localZ; uint8_t localZ;
} assetchunkentityheader_t; } assetchunkentityheader_t;
#pragma pack(pop) #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.output, "Output pointer cannot be NULL");
assertNotNull(custom.zipFile, "Zip file 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"); assertTrue(chunk->meshCount == 0, "Chunk is not in a good state");
// Read header // Read header
@@ -143,36 +143,36 @@ errorret_t assetChunkLoad(assetcustom_t custom) {
} }
// Read entity data // Read entity data
for(uint8_t i = 0; i < header.entityCount; i++) { // for(uint8_t i = 0; i < header.entityCount; i++) {
assetchunkentityheader_t entityHeader; // assetchunkentityheader_t entityHeader;
bytesRead = zip_fread( // bytesRead = zip_fread(
custom.zipFile, &entityHeader, sizeof(assetchunkentityheader_t) // custom.zipFile, &entityHeader, sizeof(assetchunkentityheader_t)
); // );
if(bytesRead != sizeof(assetchunkentityheader_t)) { // if(bytesRead != sizeof(assetchunkentityheader_t)) {
zip_fclose(custom.zipFile); // zip_fclose(custom.zipFile);
errorThrow("Failed to read chunk entity header."); // errorThrow("Failed to read chunk entity header.");
} // }
uint8_t entityIndex = entityGetAvailable(); // uint8_t entityIndex = entityGetAvailable();
if(entityIndex == 0xFF) { // if(entityIndex == 0xFF) {
zip_fclose(custom.zipFile); // zip_fclose(custom.zipFile);
errorThrow("No available entity slots."); // errorThrow("No available entity slots.");
} // }
entity_t *entity = &ENTITIES[entityIndex]; // entity_t *entity = &ENTITIES[entityIndex];
entityInit(entity, (entitytype_t)entityHeader.entityType); // entityInit(entity, (entitytype_t)entityHeader.entityType);
entity->position.x = ( // entity->position.x = (
(chunk->position.x * CHUNK_WIDTH) + entityHeader.localX // (chunk->position.x * CHUNK_WIDTH) + entityHeader.localX
); // );
entity->position.y = ( // entity->position.y = (
(chunk->position.y * CHUNK_HEIGHT) + entityHeader.localY // (chunk->position.y * CHUNK_HEIGHT) + entityHeader.localY
); // );
entity->position.z = ( // entity->position.z = (
(chunk->position.z * CHUNK_DEPTH) + entityHeader.localZ // (chunk->position.z * CHUNK_DEPTH) + entityHeader.localZ
); // );
chunk->entities[i] = entityIndex; // chunk->entities[i] = entityIndex;
} // }
errorOk(); errorOk();
} }

View File

@@ -7,14 +7,13 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "rpg/overworld/chunk.h"
typedef struct assetcustom_s assetcustom_t; 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. * @param custom The custom asset loading parameters.
* @return An error code. * @return An error code.
*/ */
errorret_t assetChunkLoad(assetcustom_t custom); errorret_t assetMapChunkHandler(assetcustom_t custom);

View File

@@ -30,6 +30,7 @@ errorret_t mapLoad(const char_t *path, const chunkpos_t position) {
if(stringCompare(MAP.filePath, path) == 0) { if(stringCompare(MAP.filePath, path) == 0) {
// Same map, no need to reload // Same map, no need to reload
errorChain(mapPositionSet(position));
errorOk(); 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"); if(last == NULL) errorThrow("Map file name has no extension");
*last = '\0'; // Terminate to remove extension *last = '\0'; // Terminate to remove extension
// Load map itself
errorChain(assetLoad(MAP.filePath, &MAP));
// Reset map position // Reset map position
MAP.chunkPosition = 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(); errorOk();
} }
@@ -181,6 +172,15 @@ void mapUpdate() {
#endif #endif
} }
void mapRender() {
if(!mapIsLoaded()) return;
textureBind(NULL);
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
mapChunkRender(&MAP.chunks[i]);
}
}
void mapDispose() { void mapDispose() {
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
mapChunkUnload(&MAP.chunks[i]); mapChunkUnload(&MAP.chunks[i]);
@@ -211,7 +211,7 @@ errorret_t mapChunkLoad(mapchunk_t* chunk) {
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
// Get chunk filepath. // 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, MAP.dirPath,
chunk->position.x, chunk->position.x,
chunk->position.y, chunk->position.y,

View File

@@ -50,6 +50,11 @@ errorret_t mapLoad(const char_t *path, const chunkpos_t position);
*/ */
void mapUpdate(); void mapUpdate();
/**
* Renders the map.
*/
void mapRender();
/** /**
* Disposes of the map. * Disposes of the map.
*/ */

View File

@@ -18,3 +18,9 @@ uint32_t mapChunkGetTileindex(const chunkpos_t position) {
bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b) { bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b) {
return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); 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);
}
}

View File

@@ -36,3 +36,10 @@ uint32_t mapChunkGetTileindex(const chunkpos_t position);
* @return true if equal, false otherwise. * @return true if equal, false otherwise.
*/ */
bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b); 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);

View File

@@ -9,6 +9,7 @@ add_subdirectory(event)
add_subdirectory(input) add_subdirectory(input)
add_subdirectory(item) add_subdirectory(item)
add_subdirectory(locale) add_subdirectory(locale)
add_subdirectory(map)
add_subdirectory(system) add_subdirectory(system)
add_subdirectory(scene) add_subdirectory(scene)
add_subdirectory(story) add_subdirectory(story)

View File

@@ -185,7 +185,25 @@ int moduleCameraIndex(lua_State *l) {
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) {
// 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_LOOKAT_PIXEL_PERFECT) {
} else if(cam->viewType == CAMERA_VIEW_TYPE_2D) { } 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; return 0;
} }

View File

@@ -13,39 +13,122 @@
void moduleGLM(scriptcontext_t *context) { void moduleGLM(scriptcontext_t *context) {
assertNotNull(context, "Context cannot be NULL."); assertNotNull(context, "Context cannot be NULL.");
// scriptStructRegister( // Create metatable for vec3 structure.
// context, if(luaL_newmetatable(context->luaState, "vec3_mt")) {
// "vec3_mt", // Metatable methods
// moduleGLMVec3Getter, lua_pushcfunction(context->luaState, moduleVec3Index);
// NULL 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);
} }
// void moduleGLMVec3Getter( lua_register(context->luaState, "vec3", moduleVec3Create);
// 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); int moduleVec3Create(lua_State *l) {
assertNotNull(l, "Lua state cannot be NULL.");
// vec3 *v = (vec3 *)structPtr; vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
// if(stringCompare(key, "x") == 0) { memoryZero(v, sizeof(vec3));
// outValue->type = SCRIPT_VALUE_TYPE_FLOAT;
// outValue->value.floatValue = (*v)[0]; // May be expecting between 1 and 3 values.
// return; int top = lua_gettop(l);
// } else if(stringCompare(key, "y") == 0) { if(top >= 1) {
// outValue->type = SCRIPT_VALUE_TYPE_FLOAT; if(!lua_isnumber(l, 1)) {
// outValue->value.floatValue = (*v)[1]; luaL_error(l, "Vec3 x component must be a number.");
// return; }
// } else if(stringCompare(key, "z") == 0) { (*v)[0] = (float_t)lua_tonumber(l, 1);
// outValue->type = SCRIPT_VALUE_TYPE_FLOAT; }
// outValue->value.floatValue = (*v)[2]; if(top >= 2) {
// return; 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;
}

View File

@@ -16,16 +16,33 @@
void moduleGLM(scriptcontext_t *context); void moduleGLM(scriptcontext_t *context);
/** /**
* Getter function for the vec3 structure. * Creates a new vec3 structure in Lua.
* *
* @param context The script context. * @param l The Lua state.
* @param key The key to get. * @return Number of return values on the Lua stack.
* @param structPtr Pointer to the vec3 structure.
* @param outValue Pointer to store the output value.
*/ */
void moduleGLMVec3Getter( int moduleVec3Create(lua_State *l);
const scriptcontext_t *context,
const char_t *key, /**
const void *structPtr, * Lua __index metamethod for vec3 structure.
scriptvalue_t *outValue *
); * @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);

View File

@@ -39,10 +39,10 @@ int moduleTextDraw(lua_State *L) {
// Optional color // Optional color
color_t *color = NULL; color_t *color = NULL;
if(lua_gettop(L) < 6 || lua_isnil(L, 6)) { if(lua_gettop(L) < 4 || lua_isnil(L, 4)) {
// Allow NULL // Allow NULL
} else if(lua_isuserdata(L, 6)) { } else if(lua_isuserdata(L, 4)) {
color = (color_t*)luaL_checkudata(L, 6, "color_mt"); color = (color_t*)luaL_checkudata(L, 4, "color_mt");
} 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

@@ -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
)

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -22,6 +22,7 @@
#include "script/module/display/moduletext.h" #include "script/module/display/moduletext.h"
#include "script/module/display/modulescreen.h" #include "script/module/display/modulescreen.h"
#include "script/module/story/modulestoryflag.h" #include "script/module/story/modulestoryflag.h"
#include "script/module/map/modulemap.h"
#include "util/string.h" #include "util/string.h"
const scriptmodule_t SCRIPT_MODULE_LIST[] = { const scriptmodule_t SCRIPT_MODULE_LIST[] = {
@@ -41,6 +42,7 @@ const scriptmodule_t SCRIPT_MODULE_LIST[] = {
{ .name = "text", .callback = moduleText }, { .name = "text", .callback = moduleText },
{ .name = "screen", .callback = moduleScreen }, { .name = "screen", .callback = moduleScreen },
{ .name = "storyflag", .callback = moduleStoryFlag }, { .name = "storyflag", .callback = moduleStoryFlag },
{ .name = "map", .callback = moduleMap },
}; };
#define SCRIPT_MODULE_COUNT ( \ #define SCRIPT_MODULE_COUNT ( \

View File

@@ -51,7 +51,7 @@ def processChunk(chunk):
# Generate binary buffer for efficient output # Generate binary buffer for efficient output
buffer = bytearray() 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(chunk.tiles).to_bytes(4, 'little')) # Number of tiles
buffer.extend(len(models).to_bytes(1, 'little')) # Number of models buffer.extend(len(models).to_bytes(1, 'little')) # Number of models
buffer.extend(len(chunk.entities).to_bytes(1, 'little')) # Number of entities buffer.extend(len(chunk.entities).to_bytes(1, 'little')) # Number of entities
@@ -92,7 +92,7 @@ def processChunk(chunk):
# Write out map file # Write out map file
relative = getAssetRelativePath(chunk.getFilename()) relative = getAssetRelativePath(chunk.getFilename())
fileNameWithoutExt = os.path.splitext(os.path.basename(relative))[0] 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) outputFilePath = os.path.join(args.output_assets, outputFileRelative)
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True) os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
with open(outputFilePath, "wb") as f: with open(outputFilePath, "wb") as f: