diff --git a/assets/map/CMakeLists.txt b/assets/map/CMakeLists.txt
index 4f7a2b6..03f4a37 100644
--- a/assets/map/CMakeLists.txt
+++ b/assets/map/CMakeLists.txt
@@ -3,4 +3,4 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
-add_asset(MAP untitled.tmx)
\ No newline at end of file
+add_asset(MAP map.json)
\ No newline at end of file
diff --git a/assets/map/map.json b/assets/map/map.json
new file mode 100644
index 0000000..25251de
--- /dev/null
+++ b/assets/map/map.json
@@ -0,0 +1,6 @@
+{
+ "tiles": [
+ 1, 1, 1,
+ 1, 1, 1
+ ]
+}
\ No newline at end of file
diff --git a/assets/map/untitled.tmx b/assets/map/untitled.tmx
deleted file mode 100644
index 9f69427..0000000
--- a/assets/map/untitled.tmx
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
diff --git a/src/asset/assettype.h b/src/asset/assettype.h
index d1fd812..e9de5e6 100644
--- a/src/asset/assettype.h
+++ b/src/asset/assettype.h
@@ -9,6 +9,7 @@
#include "type/assetpaletteimage.h"
#include "type/assetalphaimage.h"
#include "type/assetlanguage.h"
+#include "type/assetmap.h"
#include
typedef enum {
@@ -17,6 +18,7 @@ typedef enum {
ASSET_TYPE_PALETTE_IMAGE,
ASSET_TYPE_ALPHA_IMAGE,
ASSET_TYPE_LANGUAGE,
+ ASSET_TYPE_MAP,
ASSET_TYPE_COUNT,
} assettype_t;
@@ -64,5 +66,12 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
.header = "DLF",
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
.custom = assetLanguageHandler
+ },
+
+ [ASSET_TYPE_MAP] = {
+ .header = "DMF",
+ .loadStrategy = ASSET_LOAD_STRAT_ENTIRE,
+ .dataSize = sizeof(assetmap_t),
+ .entire = assetMapLoad
}
};
\ No newline at end of file
diff --git a/src/asset/type/CMakeLists.txt b/src/asset/type/CMakeLists.txt
index 1b37565..fdf5db0 100644
--- a/src/asset/type/CMakeLists.txt
+++ b/src/asset/type/CMakeLists.txt
@@ -9,4 +9,5 @@ target_sources(${DUSK_TARGET_NAME}
assetalphaimage.c
assetpaletteimage.c
assetlanguage.c
+ assetmap.c
)
\ No newline at end of file
diff --git a/src/asset/type/assetmap.c b/src/asset/type/assetmap.c
new file mode 100644
index 0000000..b4d7063
--- /dev/null
+++ b/src/asset/type/assetmap.c
@@ -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();
+}
\ No newline at end of file
diff --git a/src/asset/type/assetmap.h b/src/asset/type/assetmap.h
new file mode 100644
index 0000000..4603c2d
--- /dev/null
+++ b/src/asset/type/assetmap.h
@@ -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);
\ No newline at end of file
diff --git a/src/rpg/entity/entity.c b/src/rpg/entity/entity.c
index be46056..7db871e 100644
--- a/src/rpg/entity/entity.c
+++ b/src/rpg/entity/entity.c
@@ -81,9 +81,11 @@ void entityWalk(entity_t *entity, const entitydir_t direction) {
// Check one level down for walkable tile (stairs down)
worldpos_t belowPos = newPos;
belowPos.z -= 1;
- tile = mapGetTile(belowPos);
-
- if(tile != TILE_NULL) newPos.z -= 1;
+ tile_t belowTile = mapGetTile(belowPos);
+
+ if(belowTile == TILE_STAIRS_UP) {
+ tile = TILE_STAIRS_DOWN;// Mark current as stairs down
+ }
}
// Tile walkable?
@@ -98,11 +100,19 @@ void entityWalk(entity_t *entity, const entitydir_t direction) {
return;// Blocked
} while(++other, other < &ENTITIES[ENTITY_COUNT]);
+ entity->lastPosition = entity->position;
entity->position = newPos;
entity->animation = ENTITY_ANIM_WALK;
entity->animTime = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking
// 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) {
diff --git a/src/rpg/entity/entity.h b/src/rpg/entity/entity.h
index 8290422..0ae2184 100644
--- a/src/rpg/entity/entity.h
+++ b/src/rpg/entity/entity.h
@@ -23,6 +23,7 @@ typedef struct entity_s {
// Movement
entitydir_t direction;
worldpos_t position;
+ worldpos_t lastPosition;
entityanim_t animation;
float_t animTime;
diff --git a/src/rpg/world/map.c b/src/rpg/world/map.c
index 125c139..92e3980 100644
--- a/src/rpg/world/map.c
+++ b/src/rpg/world/map.c
@@ -128,12 +128,25 @@ void mapChunkLoad(chunk_t* chunk) {
memoryZero(chunk->tiles, sizeof(tile_t) * CHUNK_TILE_COUNT);
// 3x3 test walkable area
- for(int y = 0; y <= 3; y++) {
- for(int x = 0; x <= 3; x++) {
+ chunktileindex_t x, y, z;
+
+ z = 0;
+ for(y = 0; y <= 3; y++) {
+ for(x = 0; x <= 3; x++) {
chunktileindex_t tileIndex = (y * CHUNK_WIDTH) + x;
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) {
diff --git a/src/rpg/world/tile.c b/src/rpg/world/tile.c
index 09fe756..3e1cac1 100644
--- a/src/rpg/world/tile.c
+++ b/src/rpg/world/tile.c
@@ -8,5 +8,9 @@
#include "tile.h"
bool_t tileIsWalkable(const tile_t tile) {
- return tile == TILE_WALKABLE;
+ return (
+ tile == TILE_WALKABLE ||
+ tile == TILE_STAIRS_UP ||
+ tile == TILE_STAIRS_DOWN
+ );
}
\ No newline at end of file
diff --git a/src/rpg/world/tile.h b/src/rpg/world/tile.h
index bbabcf9..367ddfc 100644
--- a/src/rpg/world/tile.h
+++ b/src/rpg/world/tile.h
@@ -13,6 +13,8 @@ typedef uint8_t tile_t;
#define TILE_NULL 0
#define TILE_WALKABLE 1
+#define TILE_STAIRS_UP 2
+#define TILE_STAIRS_DOWN 3
/**
* Returns whether or not the given tile is walkable.
diff --git a/src/rpg/world/worldpos.h b/src/rpg/world/worldpos.h
index 401f92d..540eb2b 100644
--- a/src/rpg/world/worldpos.h
+++ b/src/rpg/world/worldpos.h
@@ -21,7 +21,7 @@
typedef int16_t worldunit_t;
typedef int16_t chunkunit_t;
typedef int16_t chunkindex_t;
-typedef uint8_t chunktileindex_t;
+typedef uint32_t chunktileindex_t;
typedef int32_t worldunits_t;
typedef int32_t chunkunits_t;
diff --git a/src/scene/scene/scenemap.c b/src/scene/scene/scenemap.c
index f71cc71..530da68 100644
--- a/src/scene/scene/scenemap.c
+++ b/src/scene/scene/scenemap.c
@@ -9,6 +9,7 @@
#include "scene/scenedata.h"
#include "display/spritebatch.h"
#include "assert/assert.h"
+#include "asset/asset.h"
#include "rpg/entity/entity.h"
#include "rpg/world/map.h"
#include "display/screen.h"
@@ -16,6 +17,7 @@
#include "util/memory.h"
#define TILE_SIZE 16
+assetmapmodel_t mesh;
errorret_t sceneMapInit(scenedata_t *data) {
// Init the camera.
@@ -34,6 +36,8 @@ errorret_t sceneMapInit(scenedata_t *data) {
);
data->sceneMap.camera.lookatPixelPerfect.pixelsPerUnit = 1.0f;
+
+ errorChain(assetLoad("map/map.dmf", &mesh));
errorOk();
}
@@ -61,15 +65,18 @@ void sceneMapEntityGetPosition(const entity_t *entity, vec3 outPosition) {
float_t animPercentage = entity->animTime / ENTITY_ANIM_WALK_DURATION;
// 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
vec3 offset = {
- x * TILE_SIZE * animPercentage,
- y * TILE_SIZE * animPercentage,
- 0.0f
+ (
+ (float_t)entity->position.x - (float_t)entity->lastPosition.x
+ ) * 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);
break;
@@ -107,7 +114,10 @@ void sceneMapRender(scenedata_t *data) {
cameraPushMatrix(&data->sceneMap.camera);
// Render map probably.
- sceneMapRenderMap();
+ // sceneMapRenderMap();
+
+ textureBind(NULL);
+ meshDraw(&mesh.mesh, -1, -1);
// Render ents
entity_t *ent = ENTITIES;
@@ -125,11 +135,10 @@ void sceneMapRenderEntity(entity_t *entity) {
if(entity->type == ENTITY_TYPE_NULL) return;
- vec3 posCenter, posMin, posMax;
- vec3 halfSize = { TILE_SIZE / 2.0f, TILE_SIZE / 2.0f, TILE_SIZE / 2.0f };
- sceneMapEntityGetPosition(entity, posCenter);
- glm_vec3_sub(posCenter, halfSize, posMin);
- glm_vec3_add(posCenter, halfSize, posMax);
+ vec3 posMin, posMax;
+ vec3 size = { TILE_SIZE, TILE_SIZE, TILE_SIZE };
+ sceneMapEntityGetPosition(entity, posMin);
+ glm_vec3_add(posMin, size, posMax);
// TEST: Change color depending on dir.
color_t testColor;
@@ -167,11 +176,6 @@ void sceneMapRenderMap() {
min[1] = chunk->position.y * CHUNK_HEIGHT * 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[1] = min[1] + (CHUNK_HEIGHT * TILE_SIZE);
max[2] = min[2];
@@ -195,4 +199,5 @@ void sceneMapRenderMap() {
}
void sceneMapDispose(scenedata_t *data) {
+ meshDispose(&mesh.mesh);
}
diff --git a/src/ui/uidebug.c b/src/ui/uidebug.c
index 76f061e..b54af32 100644
--- a/src/ui/uidebug.c
+++ b/src/ui/uidebug.c
@@ -18,7 +18,7 @@ bool_t UI_DEBUG_DRAW = true;
void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
if(!UI_DEBUG_DRAW) return;
- char_t buffer[96];
+ char_t buffer[128];
color_t color;
int32_t w, h, hOffset = 0;
@@ -64,6 +64,7 @@ void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
);
hOffset += h;
+
// Player position
entity_t *player = NULL;
for(uint8_t i = 0; i < ENTITY_COUNT; i++) {
@@ -77,9 +78,10 @@ void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
snprintf(
buffer,
sizeof(buffer),
- "%d,%d/%d/%d",
+ "%d,%d,%d/%d/%d",
player->position.x,
player->position.y,
+ player->position.z,
(int32_t)player->direction,
(int32_t)player->animation
);
@@ -87,7 +89,7 @@ void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
uiTextMeasure(buffer, tileset, &w, &h);
uiTextDraw(
SCREEN.width - w, hOffset,
- buffer, COLOR_WHITE, tileset, texture
+ buffer, COLOR_GREEN, tileset, texture
);
hOffset += h;
diff --git a/tools/assetstool/processmap.py b/tools/assetstool/processmap.py
index 64b9f7d..25b8f45 100644
--- a/tools/assetstool/processmap.py
+++ b/tools/assetstool/processmap.py
@@ -1,143 +1,124 @@
+import struct
import sys
import os
+import json
from args import args
-from xml.etree import ElementTree as ET
-from processtileset import processTileset
from assetcache import assetCache, assetGetCache
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):
cache = assetGetCache(asset['path'])
if cache is not None:
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