Prepping map stuff

This commit is contained in:
2025-11-11 12:25:46 -06:00
parent 26bfb912f1
commit 5adf8a0773
16 changed files with 249 additions and 175 deletions

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 untitled.tmx) add_asset(MAP map.json)

6
assets/map/map.json Normal file
View File

@@ -0,0 +1,6 @@
{
"tiles": [
1, 1, 1,
1, 1, 1
]
}

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="1">
<tileset firstgid="1" source="../tileset/prarie.tsx"/>
<layer id="1" name="Tile Layer 1" width="30" height="20">
<data encoding="csv">
2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
9,10,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
</map>

View File

@@ -9,6 +9,7 @@
#include "type/assetpaletteimage.h" #include "type/assetpaletteimage.h"
#include "type/assetalphaimage.h" #include "type/assetalphaimage.h"
#include "type/assetlanguage.h" #include "type/assetlanguage.h"
#include "type/assetmap.h"
#include <zip.h> #include <zip.h>
typedef enum { typedef enum {
@@ -17,6 +18,7 @@ typedef enum {
ASSET_TYPE_PALETTE_IMAGE, ASSET_TYPE_PALETTE_IMAGE,
ASSET_TYPE_ALPHA_IMAGE, ASSET_TYPE_ALPHA_IMAGE,
ASSET_TYPE_LANGUAGE, ASSET_TYPE_LANGUAGE,
ASSET_TYPE_MAP,
ASSET_TYPE_COUNT, ASSET_TYPE_COUNT,
} assettype_t; } assettype_t;
@@ -64,5 +66,12 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
.header = "DLF", .header = "DLF",
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM, .loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
.custom = assetLanguageHandler .custom = assetLanguageHandler
},
[ASSET_TYPE_MAP] = {
.header = "DMF",
.loadStrategy = ASSET_LOAD_STRAT_ENTIRE,
.dataSize = sizeof(assetmap_t),
.entire = assetMapLoad
} }
}; };

View File

@@ -9,4 +9,5 @@ target_sources(${DUSK_TARGET_NAME}
assetalphaimage.c assetalphaimage.c
assetpaletteimage.c assetpaletteimage.c
assetlanguage.c assetlanguage.c
assetmap.c
) )

33
src/asset/type/assetmap.c Normal file
View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset/asset.h"
#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");
assetmap_t *mapData = (assetmap_t *)data;
assetmapmodel_t *mesh = (assetmapmodel_t *)output;
memoryCopy(
mesh,
&mapData->models[0],
sizeof(assetmapmodel_t)
);
meshInit(
&mesh->mesh,
MESH_PRIMITIVE_TRIANGLES,
mesh->vertexCount,
mesh->vertices
);
errorOk();
}

35
src/asset/type/assetmap.h Normal file
View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "rpg/world/map.h"
#include "display/mesh/mesh.h"
typedef struct {
uint32_t vertexCount;
meshvertex_t vertices[36];
mesh_t mesh;
} assetmapmodel_t;
#pragma pack(push, 1)
typedef struct {
uint32_t tileCount;
uint8_t modelCount;
tile_t tiles[CHUNK_TILE_COUNT];
assetmapmodel_t models[1];
} assetmap_t;
#pragma pack(pop)
/**
* Loads a map asset from the given data pointer into the output map structure.
*
* @param data Pointer to the raw assetmap_t data.
* @param output Pointer to the map_t to load the map into.
* @return An error code.
*/
errorret_t assetMapLoad(void *data, void *output);

View File

@@ -81,9 +81,11 @@ void entityWalk(entity_t *entity, const entitydir_t direction) {
// Check one level down for walkable tile (stairs down) // Check one level down for walkable tile (stairs down)
worldpos_t belowPos = newPos; worldpos_t belowPos = newPos;
belowPos.z -= 1; belowPos.z -= 1;
tile = mapGetTile(belowPos); tile_t belowTile = mapGetTile(belowPos);
if(tile != TILE_NULL) newPos.z -= 1; if(belowTile == TILE_STAIRS_UP) {
tile = TILE_STAIRS_DOWN;// Mark current as stairs down
}
} }
// Tile walkable? // Tile walkable?
@@ -98,11 +100,19 @@ void entityWalk(entity_t *entity, const entitydir_t direction) {
return;// Blocked return;// Blocked
} while(++other, other < &ENTITIES[ENTITY_COUNT]); } while(++other, other < &ENTITIES[ENTITY_COUNT]);
entity->lastPosition = entity->position;
entity->position = newPos; entity->position = newPos;
entity->animation = ENTITY_ANIM_WALK; entity->animation = ENTITY_ANIM_WALK;
entity->animTime = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking entity->animTime = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking
// We are comitting, we can run effects here. // We are comitting, we can run effects here.
if(tile == TILE_STAIRS_DOWN) {
// Moving down a level
entity->position.z -= 1;
} else if(tile == TILE_STAIRS_UP) {
// Moving up a level
entity->position.z += 1;
}
} }
entity_t * entityGetAt(const worldpos_t position) { entity_t * entityGetAt(const worldpos_t position) {

View File

@@ -23,6 +23,7 @@ typedef struct entity_s {
// Movement // Movement
entitydir_t direction; entitydir_t direction;
worldpos_t position; worldpos_t position;
worldpos_t lastPosition;
entityanim_t animation; entityanim_t animation;
float_t animTime; float_t animTime;

View File

@@ -128,12 +128,25 @@ void mapChunkLoad(chunk_t* chunk) {
memoryZero(chunk->tiles, sizeof(tile_t) * CHUNK_TILE_COUNT); memoryZero(chunk->tiles, sizeof(tile_t) * CHUNK_TILE_COUNT);
// 3x3 test walkable area // 3x3 test walkable area
for(int y = 0; y <= 3; y++) { chunktileindex_t x, y, z;
for(int x = 0; x <= 3; x++) {
z = 0;
for(y = 0; y <= 3; y++) {
for(x = 0; x <= 3; x++) {
chunktileindex_t tileIndex = (y * CHUNK_WIDTH) + x; chunktileindex_t tileIndex = (y * CHUNK_WIDTH) + x;
chunk->tiles[tileIndex] = TILE_WALKABLE; chunk->tiles[tileIndex] = TILE_WALKABLE;
} }
} }
x = 3, y = 3;
chunk->tiles[(z * CHUNK_WIDTH * CHUNK_HEIGHT) + (y * CHUNK_WIDTH) + x] = TILE_STAIRS_UP;
// x = 3, y = 2, z = 1;
// chunk->tiles[(z * CHUNK_WIDTH * CHUNK_HEIGHT) + (y * CHUNK_WIDTH) + x] = TILE_WALKABLE;
// x = 2, y = 2, z = 1;
// chunk->tiles[(z * CHUNK_WIDTH * CHUNK_HEIGHT) + (y * CHUNK_WIDTH) + x] = TILE_WALKABLE;
// x = 4, y = 2, z = 1;
// chunk->tiles[(z * CHUNK_WIDTH * CHUNK_HEIGHT) + (y * CHUNK_WIDTH) + x] = TILE_WALKABLE;
} }
chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) { chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) {

View File

@@ -8,5 +8,9 @@
#include "tile.h" #include "tile.h"
bool_t tileIsWalkable(const tile_t tile) { bool_t tileIsWalkable(const tile_t tile) {
return tile == TILE_WALKABLE; return (
tile == TILE_WALKABLE ||
tile == TILE_STAIRS_UP ||
tile == TILE_STAIRS_DOWN
);
} }

View File

@@ -13,6 +13,8 @@ typedef uint8_t tile_t;
#define TILE_NULL 0 #define TILE_NULL 0
#define TILE_WALKABLE 1 #define TILE_WALKABLE 1
#define TILE_STAIRS_UP 2
#define TILE_STAIRS_DOWN 3
/** /**
* Returns whether or not the given tile is walkable. * Returns whether or not the given tile is walkable.

View File

@@ -21,7 +21,7 @@
typedef int16_t worldunit_t; typedef int16_t worldunit_t;
typedef int16_t chunkunit_t; typedef int16_t chunkunit_t;
typedef int16_t chunkindex_t; typedef int16_t chunkindex_t;
typedef uint8_t chunktileindex_t; typedef uint32_t chunktileindex_t;
typedef int32_t worldunits_t; typedef int32_t worldunits_t;
typedef int32_t chunkunits_t; typedef int32_t chunkunits_t;

View File

@@ -9,6 +9,7 @@
#include "scene/scenedata.h" #include "scene/scenedata.h"
#include "display/spritebatch.h" #include "display/spritebatch.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "asset/asset.h"
#include "rpg/entity/entity.h" #include "rpg/entity/entity.h"
#include "rpg/world/map.h" #include "rpg/world/map.h"
#include "display/screen.h" #include "display/screen.h"
@@ -16,6 +17,7 @@
#include "util/memory.h" #include "util/memory.h"
#define TILE_SIZE 16 #define TILE_SIZE 16
assetmapmodel_t mesh;
errorret_t sceneMapInit(scenedata_t *data) { errorret_t sceneMapInit(scenedata_t *data) {
// Init the camera. // Init the camera.
@@ -34,6 +36,8 @@ errorret_t sceneMapInit(scenedata_t *data) {
); );
data->sceneMap.camera.lookatPixelPerfect.pixelsPerUnit = 1.0f; data->sceneMap.camera.lookatPixelPerfect.pixelsPerUnit = 1.0f;
errorChain(assetLoad("map/map.dmf", &mesh));
errorOk(); errorOk();
} }
@@ -61,15 +65,18 @@ void sceneMapEntityGetPosition(const entity_t *entity, vec3 outPosition) {
float_t animPercentage = entity->animTime / ENTITY_ANIM_WALK_DURATION; float_t animPercentage = entity->animTime / ENTITY_ANIM_WALK_DURATION;
// Get facing rel, we know we moved from the inverse direction. // Get facing rel, we know we moved from the inverse direction.
worldunits_t x, y;
entityDirGetRelative(entity->direction, &x, &y);
x = -x, y = -y;
// Add tile size times percentage to posMin/max // Add tile size times percentage to posMin/max
vec3 offset = { vec3 offset = {
x * TILE_SIZE * animPercentage, (
y * TILE_SIZE * animPercentage, (float_t)entity->position.x - (float_t)entity->lastPosition.x
0.0f ) * TILE_SIZE * -animPercentage,
(
(float_t)entity->position.y - (float_t)entity->lastPosition.y
) * TILE_SIZE * -animPercentage,
(
(float_t)entity->position.z - (float_t)entity->lastPosition.z
) * TILE_SIZE * -animPercentage
}; };
glm_vec3_add(outPosition, offset, outPosition); glm_vec3_add(outPosition, offset, outPosition);
break; break;
@@ -107,7 +114,10 @@ void sceneMapRender(scenedata_t *data) {
cameraPushMatrix(&data->sceneMap.camera); cameraPushMatrix(&data->sceneMap.camera);
// Render map probably. // Render map probably.
sceneMapRenderMap(); // sceneMapRenderMap();
textureBind(NULL);
meshDraw(&mesh.mesh, -1, -1);
// Render ents // Render ents
entity_t *ent = ENTITIES; entity_t *ent = ENTITIES;
@@ -125,11 +135,10 @@ void sceneMapRenderEntity(entity_t *entity) {
if(entity->type == ENTITY_TYPE_NULL) return; if(entity->type == ENTITY_TYPE_NULL) return;
vec3 posCenter, posMin, posMax; vec3 posMin, posMax;
vec3 halfSize = { TILE_SIZE / 2.0f, TILE_SIZE / 2.0f, TILE_SIZE / 2.0f }; vec3 size = { TILE_SIZE, TILE_SIZE, TILE_SIZE };
sceneMapEntityGetPosition(entity, posCenter); sceneMapEntityGetPosition(entity, posMin);
glm_vec3_sub(posCenter, halfSize, posMin); glm_vec3_add(posMin, size, posMax);
glm_vec3_add(posCenter, halfSize, posMax);
// TEST: Change color depending on dir. // TEST: Change color depending on dir.
color_t testColor; color_t testColor;
@@ -167,11 +176,6 @@ void sceneMapRenderMap() {
min[1] = chunk->position.y * CHUNK_HEIGHT * TILE_SIZE; min[1] = chunk->position.y * CHUNK_HEIGHT * TILE_SIZE;
min[2] = chunk->position.z * CHUNK_DEPTH * TILE_SIZE; min[2] = chunk->position.z * CHUNK_DEPTH * TILE_SIZE;
// center tile
min[0] -= TILE_SIZE / 2.0f;
min[1] -= TILE_SIZE / 2.0f;
min[2] -= TILE_SIZE / 2.0f;
max[0] = min[0] + (CHUNK_WIDTH * TILE_SIZE); max[0] = min[0] + (CHUNK_WIDTH * TILE_SIZE);
max[1] = min[1] + (CHUNK_HEIGHT * TILE_SIZE); max[1] = min[1] + (CHUNK_HEIGHT * TILE_SIZE);
max[2] = min[2]; max[2] = min[2];
@@ -195,4 +199,5 @@ void sceneMapRenderMap() {
} }
void sceneMapDispose(scenedata_t *data) { void sceneMapDispose(scenedata_t *data) {
meshDispose(&mesh.mesh);
} }

View File

@@ -18,7 +18,7 @@ bool_t UI_DEBUG_DRAW = true;
void uiDebugRender(const tileset_t *tileset, texture_t *texture) { void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
if(!UI_DEBUG_DRAW) return; if(!UI_DEBUG_DRAW) return;
char_t buffer[96]; char_t buffer[128];
color_t color; color_t color;
int32_t w, h, hOffset = 0; int32_t w, h, hOffset = 0;
@@ -64,6 +64,7 @@ void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
); );
hOffset += h; hOffset += h;
// Player position // Player position
entity_t *player = NULL; entity_t *player = NULL;
for(uint8_t i = 0; i < ENTITY_COUNT; i++) { for(uint8_t i = 0; i < ENTITY_COUNT; i++) {
@@ -77,9 +78,10 @@ void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
snprintf( snprintf(
buffer, buffer,
sizeof(buffer), sizeof(buffer),
"%d,%d/%d/%d", "%d,%d,%d/%d/%d",
player->position.x, player->position.x,
player->position.y, player->position.y,
player->position.z,
(int32_t)player->direction, (int32_t)player->direction,
(int32_t)player->animation (int32_t)player->animation
); );
@@ -87,7 +89,7 @@ void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
uiTextMeasure(buffer, tileset, &w, &h); uiTextMeasure(buffer, tileset, &w, &h);
uiTextDraw( uiTextDraw(
SCREEN.width - w, hOffset, SCREEN.width - w, hOffset,
buffer, COLOR_WHITE, tileset, texture buffer, COLOR_GREEN, tileset, texture
); );
hOffset += h; hOffset += h;

View File

@@ -1,143 +1,124 @@
import struct
import sys import sys
import os import os
import json
from args import args from args import args
from xml.etree import ElementTree as ET
from processtileset import processTileset
from assetcache import assetCache, assetGetCache from assetcache import assetCache, assetGetCache
from assethelpers import getAssetRelativePath from assethelpers import getAssetRelativePath
CHUNK_WIDTH = 16
CHUNK_HEIGHT = 16
CHUNK_DEPTH = 32
CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH
TILE_SIZE = 16.0
def createQuadForTile(model, tileIndex, x=0, y=0, z=0):
# Only append vertices if z == 0
if z != 0:
return
# Determine color for checkerboard pattern
color = (255,255,255) if (x + y) % 2 == 0 else (0,0,0)
# Use TILE_SIZE for positions
px = x * TILE_SIZE
py = y * TILE_SIZE
pz = z * TILE_SIZE
quad_vertices = [
{'position': (px, py, pz), 'color': color, 'uv': (0,0)}, # 0,0
{'position': (px + TILE_SIZE, py, pz), 'color': color, 'uv': (1,0)}, # 1,0
{'position': (px + TILE_SIZE, py + TILE_SIZE, pz), 'color': color, 'uv': (1,1)}, # 1,1
{'position': (px, py, pz), 'color': color, 'uv': (0,0)}, # 0,0 (repeat)
{'position': (px + TILE_SIZE, py + TILE_SIZE, pz), 'color': color, 'uv': (1,1)}, # 1,1 (repeat)
{'position': (px, py + TILE_SIZE, pz), 'color': color, 'uv': (0,1)} # 0,1
]
base = len(model['vertices'])
quad_indices = [base, base+1, base+2, base+3, base+4, base+5]
model['vertices'].extend(quad_vertices)
model['indices'].extend(quad_indices)
model['vertexCount'] = len(model['vertices'])
model['indexCount'] = len(model['indices'])
def processMap(asset): def processMap(asset):
cache = assetGetCache(asset['path']) cache = assetGetCache(asset['path'])
if cache is not None: if cache is not None:
return cache return cache
# Load the TMX file
tree = ET.parse(asset['path'])
root = tree.getroot()
# Root needs to be "map" element.
if root.tag != 'map':
print(f"Error: TMX file {asset['path']} does not have a <map> root element")
sys.exit(1)
# Root needs to be orientation="orthogonal"
if 'orientation' not in root.attrib or root.attrib['orientation'] != 'orthogonal':
print(f"Error: TMX file {asset['path']} does not have orientation='orthogonal'")
sys.exit(1)
# Extract width, height, tilewidth, tileheight attributes # Read input file as JSON
if 'width' not in root.attrib or 'height' not in root.attrib or 'tilewidth' not in root.attrib or 'tileheight' not in root.attrib: with open(asset['path'], 'r') as f:
print(f"Error: TMX file {asset['path']} is missing required attributes (width, height, tilewidth, tileheight)") inData = json.load(f)
sys.exit(1)
mapWidth = int(root.attrib['width'])
mapHeight = int(root.attrib['height'])
tileWidth = int(root.attrib['tilewidth'])
tileHeight = int(root.attrib['tileheight'])
# Find all tileset elements
tilesets = []
for tilesetElement in root.findall('tileset'):
# Tileset must have a source attribute
if 'source' not in tilesetElement.attrib:
print(f"Error: <tileset> element in {asset['path']} is missing a source attribute")
sys.exit(1)
# Must have a firstgid attribute
if 'firstgid' not in tilesetElement.attrib:
print(f"Error: <tileset> element in {asset['path']} is missing a firstgid attribute")
sys.exit(1)
firstGid = int(tilesetElement.attrib['firstgid'])
source = tilesetElement.attrib['source']
# Get source path relative to the tmx file's working directory.
# Needs normalizing also since ".." is often used.
source = os.path.normpath(os.path.join(os.path.dirname(asset['path']), source))
tileset = processTileset({ 'path': source, 'type': 'tileset', 'options': {} })
tilesets.append({
'firstGid': firstGid,
'source': source,
'tileset': tileset
})
# Sort tilesets by firstGid, highest first
tilesets.sort(key=lambda x: x['firstGid'], reverse=True)
# Layer types
# objectLayers = [] # Not implemented
tileLayers = []
for layerElement in root.findall('layer'):
# Assume tile layer for now
# Must have id, name, width, height attributes
if 'id' not in layerElement.attrib or 'name' not in layerElement.attrib or 'width' not in layerElement.attrib or 'height' not in layerElement.attrib:
print(f"Error: <layer> element in {asset['path']} is missing required attributes (id, name, width, height)")
sys.exit(1)
id = int(layerElement.attrib['id'])
name = layerElement.attrib['name']
width = int(layerElement.attrib['width'])
height = int(layerElement.attrib['height'])
# Need exactly one data element
dataElements = layerElement.findall('data')
if len(dataElements) != 1:
print(f"Error: <layer> element in {asset['path']} must have exactly one <data> child element")
sys.exit(1)
# Get text, remove whitespace, split by comman and convert to int
dataElement = dataElements[0]
if dataElement.attrib.get('encoding', '') != 'csv':
print(f"Error: <data> element in {asset['path']} must have encoding='csv'")
sys.exit(1)
dataText = dataElement.text.strip()
data = [int(gid) for gid in dataText.split(',') if gid.strip().isdigit()]
# Should be exactly width * height entries
if len(data) != width * height:
print(f"Error: <data> element in {asset['path']} has {len(data)} entries but expected {width * height} (width * height)")
sys.exit(1)
tileLayers.append({
'id': id,
'name': name,
'width': width,
'height': height,
'data': data,
})
# Now we have our layers all parsed out.
data = bytearray()
data += b'DRM' # Dusk RPG Map
data += mapWidth.to_bytes(4, 'little') # Map width in tiles
data += mapHeight.to_bytes(4, 'little') # Map height in tiles
data += len(tilesets).to_bytes(4, 'little') # Number of tilesets
data += len(tileLayers).to_bytes(4, 'little') # Number of layers
# For each layer... tileIndexes = inData['tiles']
for layer in tileLayers:
for gid in layer['data']:
data += gid.to_bytes(4, 'little') # Tileset index
# For each tileset # Create output object 'map' with default tile indexes and models array
for tileset in tilesets: map = {
data += tileset['firstGid'].to_bytes(4, 'little') # First GID 'tiles': [0] * CHUNK_TILE_COUNT,
data += tileset['tileset']['tilesetIndex'].to_bytes(4, 'little') # Tileset index 'models': []
}
# Create a simple 3D model object
model = {
'vertices': [],
'indices': [],
'vertexCount': 0,
'indexCount': 0
}
# Append the model to map.models
map['models'].append(model)
for i, tile in enumerate(tileIndexes):
# Calculate x, y, z from i
x = i % CHUNK_WIDTH
y = (i // CHUNK_WIDTH) % CHUNK_HEIGHT
z = i // (CHUNK_WIDTH * CHUNK_HEIGHT)
createQuadForTile(model, tile, x, y, z)
# Generate binary buffer for efficient output
buffer = bytearray()
buffer.extend(b'DMF')# Header
buffer.extend(len(map['tiles']).to_bytes(4, 'little')) # Number of tiles
buffer.extend(len(map['models']).to_bytes(1, 'little')) # Number of models
# Buffer tile data as array of uint8_t
for tileIndex in map['tiles']:
buffer.append(tileIndex.to_bytes(1, 'little')[0])
# For each model
for model in map['models']:
# Write vertex count and index count
buffer.extend(model['vertexCount'].to_bytes(4, 'little'))
# buffer.extend(model['indexCount'].to_bytes(4, 'little'))
# For each vertex
for vertex in model['vertices']:
# This is not tightly packed in memory.
# R G B A U V X Y Z
# Color is 4 bytes (RGBA)
# Rest is floats
r, g, b = vertex['color']
a = 255
buffer.extend(r.to_bytes(1, 'little'))
buffer.extend(g.to_bytes(1, 'little'))
buffer.extend(b.to_bytes(1, 'little'))
buffer.extend(a.to_bytes(1, 'little'))
u, v = vertex['uv']
buffer.extend(bytearray(struct.pack('<f', u)))
buffer.extend(bytearray(struct.pack('<f', v)))
x, y, z = vertex['position']
buffer.extend(bytearray(struct.pack('<f', x)))
buffer.extend(bytearray(struct.pack('<f', y)))
buffer.extend(bytearray(struct.pack('<f', z)))
# Write out map file
relative = getAssetRelativePath(asset['path']) relative = getAssetRelativePath(asset['path'])
fileNameWithoutExt = os.path.splitext(os.path.basename(asset['path']))[0] fileNameWithoutExt = os.path.splitext(os.path.basename(asset['path']))[0]
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.drm") outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dmf")
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:
f.write(data) f.write(buffer)
outMap = { outMap = {
'mapPath': outputFileRelative,
'files': [ outputFilePath ], 'files': [ outputFilePath ],
'width': mapWidth, 'map': map
'height': mapHeight,
} }
return assetCache(asset['path'], outMap) return assetCache(asset['path'], outMap)