From 6f33522c1cd3b18bbc069bab77706a84bdc70194 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sat, 22 Nov 2025 10:38:16 -0600 Subject: [PATCH] Begin adding entities to editor --- src/asset/type/assetchunk.c | 52 +++++++++++++++++++++ src/duskdefs.env | 8 ++++ src/rpg/entity/entity.c | 9 ++++ src/rpg/entity/entity.h | 11 +++-- src/rpg/entity/entitytype.h | 10 +--- src/rpg/rpg.c | 10 ++-- src/rpg/world/chunk.h | 1 + src/rpg/world/map.c | 7 +++ tools/assetstool/processmap.py | 9 ++++ tools/dusk/chunk.py | 21 ++++++++- tools/dusk/defs.py | 9 +++- tools/dusk/entity.py | 9 ++++ tools/dusk/map.py | 1 + tools/editortool/map/chunkpanel.py | 28 +---------- tools/editortool/map/entitypanel.py | 70 ++++++++++++++++++++++++++++ tools/editortool/map/mapleftpanel.py | 46 ++++++++++++++++++ tools/editortool/maptool.py | 16 +++---- 17 files changed, 261 insertions(+), 56 deletions(-) create mode 100644 tools/dusk/entity.py create mode 100644 tools/editortool/map/entitypanel.py create mode 100644 tools/editortool/map/mapleftpanel.py diff --git a/src/asset/type/assetchunk.c b/src/asset/type/assetchunk.c index 205f3c4..d4e8063 100644 --- a/src/asset/type/assetchunk.c +++ b/src/asset/type/assetchunk.c @@ -7,11 +7,13 @@ #include "asset/asset.h" #include "assert/assert.h" +#include "rpg/entity/entity.h" #pragma pack(push, 1) typedef struct { uint32_t tileCount; uint8_t modelCount; + uint8_t entityCount; } assetchunkheader_t; #pragma pack(pop) @@ -27,6 +29,15 @@ typedef struct { } assetchunkmodelheader_t; #pragma pack(pop) +#pragma pack(push, 1) +typedef struct { + entitytype_t entityType; + uint8_t localX; + uint8_t localY; + uint8_t localZ; +} assetchunkentityheader_t; +#pragma pack(pop) + errorret_t assetChunkLoad(assetcustom_t custom) { assertNotNull(custom.output, "Output pointer cannot be NULL"); assertNotNull(custom.zipFile, "Zip file pointer cannot be NULL"); @@ -62,6 +73,15 @@ errorret_t assetChunkLoad(assetcustom_t custom) { ); } + if(header.entityCount > CHUNK_ENTITY_COUNT_MAX) { + zip_fclose(custom.zipFile); + errorThrow( + "Chunk asset has too many entities: %d (max %d).", + header.entityCount, + CHUNK_ENTITY_COUNT_MAX + ); + } + chunk->meshCount = header.modelCount; // Read tile data @@ -122,5 +142,37 @@ 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."); + } + + 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 + ); + + chunk->entities[i] = entityIndex; + } + errorOk(); } \ No newline at end of file diff --git a/src/duskdefs.env b/src/duskdefs.env index 1b5880f..b523bc5 100644 --- a/src/duskdefs.env +++ b/src/duskdefs.env @@ -8,12 +8,20 @@ ENTITY_DIR_WEST = 1 ENTITY_DIR_EAST = 2 ENTITY_DIR_NORTH = 3 +ENTITY_COUNT = 256 + +ENTITY_TYPE_NULL = 0 +ENTITY_TYPE_PLAYER = 1 +ENTITY_TYPE_NPC = 2 +ENTITY_TYPE_COUNT = 3 + CHUNK_WIDTH = 16 CHUNK_HEIGHT = 16 CHUNK_DEPTH = 4 # CHUNK_VERTEX_COUNT_MAX = QUAD_VERTEXES * CHUNK_WIDTH * CHUNK_HEIGHT * 4 CHUNK_VERTEX_COUNT_MAX=6144 CHUNK_MESH_COUNT_MAX = 14 +CHUNK_ENTITY_COUNT_MAX = 8 TILE_WIDTH = 16.0 TILE_HEIGHT = 16.0 diff --git a/src/rpg/entity/entity.c b/src/rpg/entity/entity.c index fc158a8..77d2b54 100644 --- a/src/rpg/entity/entity.c +++ b/src/rpg/entity/entity.c @@ -188,4 +188,13 @@ entity_t * entityGetAt(const worldpos_t position) { } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); return NULL; +} + +uint8_t entityGetAvailable() { + entity_t *ent = ENTITIES; + do { + if(ent->type == ENTITY_TYPE_NULL) return ent - ENTITIES; + } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); + + return 0xFF; } \ No newline at end of file diff --git a/src/rpg/entity/entity.h b/src/rpg/entity/entity.h index 0ae2184..8cf7343 100644 --- a/src/rpg/entity/entity.h +++ b/src/rpg/entity/entity.h @@ -11,8 +11,6 @@ #include "entitytype.h" #include "npc.h" -#define ENTITY_COUNT 256 - typedef struct map_s map_t; typedef struct entity_s { @@ -69,4 +67,11 @@ void entityWalk(entity_t *entity, const entitydir_t direction); * @param pos The world position to check. * @return Pointer to the entity at the position, or NULL if none. */ -entity_t *entityGetAt(const worldpos_t pos); \ No newline at end of file +entity_t *entityGetAt(const worldpos_t pos); + +/** + * Gets an available entity index. + * + * @return The index of an available entity, or 0xFF if none are available. + */ +uint8_t entityGetAvailable(); \ No newline at end of file diff --git a/src/rpg/entity/entitytype.h b/src/rpg/entity/entitytype.h index 6ed82d3..0e3e4ba 100644 --- a/src/rpg/entity/entitytype.h +++ b/src/rpg/entity/entitytype.h @@ -6,17 +6,11 @@ */ #pragma once +#include "duskdefs.h" #include "rpg/entity/player.h" #include "npc.h" -typedef enum { - ENTITY_TYPE_NULL = 0, - - ENTITY_TYPE_PLAYER, - ENTITY_TYPE_NPC, - - ENTITY_TYPE_COUNT -} entitytype_t; +typedef uint8_t entitytype_t; typedef union { player_t player; diff --git a/src/rpg/rpg.c b/src/rpg/rpg.c index 0c3a454..01f4393 100644 --- a/src/rpg/rpg.c +++ b/src/rpg/rpg.c @@ -13,6 +13,7 @@ #include "rpgcamera.h" #include "rpgtextbox.h" #include "util/memory.h" +#include "assert/assert.h" errorret_t rpgInit(void) { memoryZero(ENTITIES, sizeof(ENTITIES)); @@ -26,17 +27,14 @@ errorret_t rpgInit(void) { rpgTextboxInit(); // TEST: Create some entities. - entity_t *ent; - ent = &ENTITIES[0]; + uint8_t entIndex = entityGetAvailable(); + assertTrue(entIndex != 0xFF, "No available entity slots!."); + entity_t *ent = &ENTITIES[entIndex]; entityInit(ent, ENTITY_TYPE_PLAYER); RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY; RPG_CAMERA.followEntity.followEntityId = ent->id; ent->position.x = 2, ent->position.y = 2; - ent = &ENTITIES[1]; - entityInit(ent, ENTITY_TYPE_NPC); - ent->position.x = 4, ent->position.y = 0, ent->position.z = 1; - // All Good! errorOk(); } diff --git a/src/rpg/world/chunk.h b/src/rpg/world/chunk.h index 9187ced..7f1079b 100644 --- a/src/rpg/world/chunk.h +++ b/src/rpg/world/chunk.h @@ -17,6 +17,7 @@ typedef struct chunk_s { uint8_t meshCount; meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX]; mesh_t meshes[CHUNK_MESH_COUNT_MAX]; + uint8_t entities[CHUNK_ENTITY_COUNT_MAX]; } chunk_t; /** diff --git a/src/rpg/world/map.c b/src/rpg/world/map.c index 9c0b30d..afe66e5 100644 --- a/src/rpg/world/map.c +++ b/src/rpg/world/map.c @@ -9,6 +9,7 @@ #include "util/memory.h" #include "assert/assert.h" #include "asset/asset.h" +#include "rpg/entity/entity.h" map_t MAP; @@ -126,6 +127,11 @@ void mapDispose() { } void mapChunkUnload(chunk_t* chunk) { + for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) { + if(chunk->entities[i] == 0xFF) break; + entity_t *entity = &ENTITIES[chunk->entities[i]]; + entity->type = ENTITY_TYPE_NULL; + } for(uint8_t i = 0; i < chunk->meshCount; i++) { if(chunk->meshes[i].vertexCount == 0) continue; meshDispose(&chunk->meshes[i]); @@ -137,6 +143,7 @@ errorret_t mapChunkLoad(chunk_t* chunk) { chunk->meshCount = 0; memoryZero(chunk->meshes, sizeof(chunk->meshes)); + memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); snprintf(buffer, sizeof(buffer), "map/map/%d_%d_%d.dcf", chunk->position.x, diff --git a/tools/assetstool/processmap.py b/tools/assetstool/processmap.py index f4a0ea0..7472487 100644 --- a/tools/assetstool/processmap.py +++ b/tools/assetstool/processmap.py @@ -54,6 +54,7 @@ def processChunk(chunk): buffer.extend(b'DCF')# 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 # Buffer tile data as array of uint8_t for tileIndex, tile in chunk.tiles.items(): @@ -79,6 +80,14 @@ def processChunk(chunk): buffer.extend(bytearray(struct.pack('