Map exec
Some checks failed
Build Dusk / build-linux (push) Successful in 1m40s
Build Dusk / build-psp (push) Failing after 1m38s

This commit is contained in:
2025-12-26 20:38:24 +10:00
parent 7940f4c487
commit 726233e55f
29 changed files with 152 additions and 33 deletions

View File

@@ -1,6 +1,7 @@
module('platform') module('platform')
module('input') module('input')
module('scene') module('scene')
module('map')
-- Default Input bindings. -- Default Input bindings.
if PLATFORM == "psp" then if PLATFORM == "psp" then
@@ -37,4 +38,5 @@ else
end end
end end
sceneSet('map') sceneSet('map')
mapLoad('map/testmap/testmap.dmf')

View File

@@ -3,4 +3,4 @@
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_asset(MAP map.json) add_subdirectory(testmap)

View File

@@ -1,3 +0,0 @@
{
"mapName": "Test"
}

View File

@@ -0,0 +1,7 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_asset(MAP testmap.json)
add_asset(SCRIPT testmap.lua)

View File

@@ -0,0 +1,3 @@
{
"name": "Test"
}

View File

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

View File

@@ -1 +1 @@
print('map') -- Map Scene

View File

@@ -89,5 +89,5 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
.header = "DSF", .header = "DSF",
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM, .loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
.custom = assetScriptHandler .custom = assetScriptHandler
} },
}; };

View File

@@ -11,6 +11,7 @@
#include "asset/asset.h" #include "asset/asset.h"
#include "rpg/entity/entity.h" #include "rpg/entity/entity.h"
#include "util/string.h" #include "util/string.h"
#include "script/scriptcontext.h"
map_t MAP; map_t MAP;
@@ -44,6 +45,19 @@ errorret_t mapLoad(const char_t *path, const chunkpos_t position) {
// Store the map file path // Store the map file path
stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX); stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX);
// Determine directory path (it is dirname)
stringCopy(MAP.dirPath, path, MAP_FILE_PATH_MAX);
char_t *last = stringFindLastChar(MAP.dirPath, '/');
if(last == NULL) errorThrow("Invalid map file path");
// Store filename, sans extension
stringCopy(MAP.fileName, last + 1, MAP_FILE_PATH_MAX);
*last = '\0'; // Terminate to get directory path
last = stringFindLastChar(MAP.fileName, '.');
if(last == NULL) errorThrow("Map file name has no extension");
*last = '\0'; // Terminate to remove extension
// Reset map position // Reset map position
MAP.chunkPosition = position; MAP.chunkPosition = position;
@@ -64,6 +78,18 @@ errorret_t mapLoad(const char_t *path, const chunkpos_t position) {
} }
// Execute map script. // 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();
} }
@@ -183,8 +209,8 @@ 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/%d_%d_%d.dcf", snprintf(buffer, sizeof(buffer), "%s/chunks/%d_%d_%d.dcf",
MAP.filePath, MAP.dirPath,
chunk->position.x, chunk->position.x,
chunk->position.y, chunk->position.y,
chunk->position.z chunk->position.z

View File

@@ -12,6 +12,9 @@
typedef struct map_s { typedef struct map_s {
char_t filePath[MAP_FILE_PATH_MAX]; char_t filePath[MAP_FILE_PATH_MAX];
char_t dirPath[MAP_FILE_PATH_MAX];
char_t fileName[MAP_FILE_PATH_MAX];
chunk_t chunks[MAP_CHUNK_COUNT]; chunk_t chunks[MAP_CHUNK_COUNT];
chunk_t *chunkOrder[MAP_CHUNK_COUNT]; chunk_t *chunkOrder[MAP_CHUNK_COUNT];
chunkpos_t chunkPosition; chunkpos_t chunkPosition;

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
#include "debug/debug.h"
#include "assert/assert.h"
#include "rpg/world/map.h"
int32_t moduleMapLoad(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) {
luaL_error(L, "Expected string map filename");
return 0;
}
// Potentially provide up to 3 params
chunkpos_t initial = { .x = 0, .y = 0, .z = 0 };
if(lua_isnumber(L, 2)) {
initial.x = (chunkunit_t)luaL_checkinteger(L, 2);
}
if(lua_isnumber(L, 3)) {
initial.y = (chunkunit_t)luaL_checkinteger(L, 3);
}
if(lua_isnumber(L, 4)) {
initial.z = (chunkunit_t)luaL_checkinteger(L, 4);
}
// Load the map.
errorret_t ret = mapLoad(luaL_checkstring(L, 1), initial);
if(ret.code != ERROR_OK) {
luaL_error(L, "Failed to load map");
errorCatch(errorPrint(ret));
return 0;
}
return 0;
}
void moduleMapSystem(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL");
scriptContextRegFunc(context, "mapLoad", moduleMapLoad);
}

View File

@@ -10,12 +10,14 @@
#include "script/module/moduleinput.h" #include "script/module/moduleinput.h"
#include "script/module/moduleplatform.h" #include "script/module/moduleplatform.h"
#include "script/module/modulescene.h" #include "script/module/modulescene.h"
#include "script/module/modulemap.h"
const scriptmodule_t SCRIPT_MODULE_LIST[] = { const scriptmodule_t SCRIPT_MODULE_LIST[] = {
{ .name = "system", .callback = moduleSystem }, { .name = "system", .callback = moduleSystem },
{ .name = "input", .callback = moduleInput }, { .name = "input", .callback = moduleInput },
{ .name = "platform", .callback = modulePlatform }, { .name = "platform", .callback = modulePlatform },
{ .name = "scene", .callback = moduleScene }, { .name = "scene", .callback = moduleScene },
{ .name = "map", .callback = moduleMapSystem },
}; };
#define SCRIPT_MODULE_COUNT ( \ #define SCRIPT_MODULE_COUNT ( \

View File

@@ -57,6 +57,17 @@ char_t * stringToken(char_t *str, const char_t *delim) {
return strtok(str, delim); return strtok(str, delim);
} }
char_t * stringFindLastChar(const char_t *str, const char_t c) {
assertNotNull(str, "str must not be NULL");
char_t *last = NULL;
for(const char_t *p = str; *p != '\0'; p++) {
if(*p == c) {
last = (char_t *)p;
}
}
return last;
}
int32_t stringFormat( int32_t stringFormat(
char_t *dest, char_t *dest,
const size_t destSize, const size_t destSize,

View File

@@ -64,6 +64,16 @@ void stringTrim(char_t *str);
*/ */
char_t * stringToken(char_t *str, const char_t *delim); char_t * stringToken(char_t *str, const char_t *delim);
/**
* Finds the last occurrence of a character in a string.
*
* @param str The string to search.
* @param c The character to find.
* @return A pointer to the last occurrence of the character in the string, or
* NULL if not found.
*/
char_t * stringFindLastChar(const char_t *str, const char_t c);
/** /**
* Formats a string. * Formats a string.
* *

View File

@@ -111,11 +111,11 @@ def processMap(asset):
map = Map(None) map = Map(None)
map.load(asset['path']) map.load(asset['path'])
dir = map.getMapDirectory() chunksDir = map.getChunkDirectory()
files = os.listdir(dir) files = os.listdir(chunksDir)
if len(files) == 0: if len(files) == 0:
print(f"Error: No chunk files found in map directory {dir}.") print(f"Error: No chunk files found in {chunksDir}.")
sys.exit(1) sys.exit(1)
chunkFiles = [] chunkFiles = []
@@ -133,21 +133,22 @@ def processMap(asset):
result = processChunk(chunk) result = processChunk(chunk)
chunkFiles.extend(result['files']) chunkFiles.extend(result['files'])
outMap = { # Map file
'files': chunkFiles outBuffer = bytearray()
} outBuffer.extend(b'DMF')
return assetCache(asset['path'], outMap) outBuffer.extend(len(chunkFiles).to_bytes(4, 'little'))
# List files # DMF (Dusk Map file)
chunkFiles = [] fileRelative = getAssetRelativePath(asset['path'])
for fileName in os.listdir(asset['path']): fileNameWithoutExt = os.path.splitext(os.path.basename(fileRelative))[0]
if not fileName.endswith('.json'): outputMapRelative = os.path.join(os.path.dirname(fileRelative), f"{fileNameWithoutExt}.dmf")
continue outputMapPath = os.path.join(args.output_assets, outputMapRelative)
result = processChunk(os.path.join(asset['path'], fileName)) os.makedirs(os.path.dirname(outputMapPath), exist_ok=True)
chunkFiles.extend(result['files']) with open(outputMapPath, "wb") as f:
f.write(outBuffer)
outMap = { outMap = {
'files': chunkFiles 'files': chunkFiles
} }
outMap['files'].append(outputMapPath)
return assetCache(asset['path'], outMap) return assetCache(asset['path'], outMap)

View File

@@ -131,12 +131,12 @@ class Chunk:
return self.dirty return self.dirty
def getFilename(self): def getFilename(self):
if not self.map or not hasattr(self.map, 'getMapDirectory'): if not self.map or not hasattr(self.map, 'getChunkDirectory'):
return None return None
dir_path = self.map.getMapDirectory() dirPath = self.map.getChunkDirectory()
if dir_path is None: if dirPath is None:
return None return None
return f"{dir_path}/{self.x}_{self.y}_{self.z}.json" return f"{dirPath}/{self.x}_{self.y}_{self.z}.json"
def draw(self): def draw(self):
self.vertexBuffer.draw() self.vertexBuffer.draw()

View File

@@ -1,4 +1,5 @@
import json import json
import sys
from dusk.event import Event from dusk.event import Event
from PyQt5.QtWidgets import QFileDialog, QMessageBox from PyQt5.QtWidgets import QFileDialog, QMessageBox
from PyQt5.QtCore import QTimer from PyQt5.QtCore import QTimer
@@ -129,11 +130,17 @@ class Map:
return self.mapFileName if self.mapFileName and os.path.exists(self.mapFileName) else None return self.mapFileName if self.mapFileName and os.path.exists(self.mapFileName) else None
def getMapDirectory(self): def getMapDirectory(self):
fname = self.getMapFilename() if self.mapFileName is None:
if not fname or not fname.endswith('.json'):
return None return None
return fname[:-5] # Remove '.json' extension dirname = os.path.dirname(self.mapFileName)
return dirname
def getChunkDirectory(self):
dirName = self.getMapDirectory()
if dirName is None:
return None
return os.path.join(dirName, 'chunks')
def anyChunksDirty(self): def anyChunksDirty(self):
for chunk in self.chunks.values(): for chunk in self.chunks.values():
if chunk.isDirty(): if chunk.isDirty():