diff --git a/include/dungeon/dgn.h b/include/dungeon/dgn.h index eef0dce..965c414 100644 --- a/include/dungeon/dgn.h +++ b/include/dungeon/dgn.h @@ -109,6 +109,8 @@ struct dgn { struct dgn *dgn_parse(uint8_t *data, size_t size); void dgn_free(struct dgn *dgn); + +int dgn_cell_index(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z); struct dgn_cell *dgn_cell_at(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z); static inline bool dgn_is_in_map(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z) diff --git a/include/dungeon/map.h b/include/dungeon/map.h index dde1b66..105e046 100644 --- a/include/dungeon/map.h +++ b/include/dungeon/map.h @@ -17,6 +17,7 @@ #ifndef SYSTEM4_DUNGEON_MAP_H #define SYSTEM4_DUNGEON_MAP_H +struct page; struct dungeon_context; struct dungeon_map; @@ -32,5 +33,7 @@ void dungeon_map_set_cg(int surface, int index, int sprite); void dungeon_map_set_small_map_floor(int surface, int floor); void dungeon_map_set_large_map_floor(int surface, int floor); void dungeon_map_set_walked(int surface, int x, int y, int z, int flag); +bool dungeon_map_load_walk_data(int surface, int map, struct page **page); +bool dungeon_map_save_walk_data(int surface, int map, struct page **page); #endif /* SYSTEM4_DUNGEON_MAP_H */ diff --git a/src/dungeon/dgn.c b/src/dungeon/dgn.c index c840e80..fa30762 100644 --- a/src/dungeon/dgn.c +++ b/src/dungeon/dgn.c @@ -114,9 +114,14 @@ void dgn_free(struct dgn *dgn) free(dgn); } -struct dgn_cell *dgn_cell_at(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z) +int dgn_cell_index(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z) { if (x >= dgn->size_x || y >= dgn->size_y || z >= dgn->size_z) VM_ERROR("cell location out of range: (%d, %d, %d)", x, y, z); - return &dgn->cells[(z * dgn->size_y + y) * dgn->size_x + x]; + return (z * dgn->size_y + y) * dgn->size_x + x; +} + +struct dgn_cell *dgn_cell_at(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z) +{ + return &dgn->cells[dgn_cell_index(dgn, x, y, z)]; } diff --git a/src/dungeon/map.c b/src/dungeon/map.c index 83e9d21..f2de17d 100644 --- a/src/dungeon/map.c +++ b/src/dungeon/map.c @@ -24,6 +24,7 @@ #include "gfx/gfx.h" #include "sact.h" #include "vm.h" +#include "vm/page.h" #ifndef M_PI #define M_PI (3.14159265358979323846) @@ -56,6 +57,7 @@ struct dungeon_map { int small_map_floor; int large_map_floor; bool all_visible; + int *walk_data; }; struct dungeon_map *dungeon_map_create(void) @@ -71,6 +73,7 @@ void dungeon_map_free(struct dungeon_map *map) for (int i = 0; i < map->nr_textures; i++) gfx_delete_texture(&map->textures[i]); free(map->textures); + free(map->walk_data); free(map); } @@ -81,10 +84,11 @@ void dungeon_map_init(struct dungeon_context *ctx) map->textures = xcalloc(dgn->size_y, sizeof(struct texture)); map->nr_textures = dgn->size_y; - for (int y = 0; y < dgn->size_y; y++) { + map->walk_data = xcalloc(dgn->size_x * dgn->size_y * dgn->size_z, sizeof(int)); + for (uint32_t y = 0; y < dgn->size_y; y++) { gfx_init_texture_rgba(&map->textures[y], dgn->size_x * CS, dgn->size_z * CS, (SDL_Color){0, 0, 0, 0}); - for (int z = 0; z < dgn->size_z; z++) { - for (int x = 0; x < dgn->size_x; x++) { + for (uint32_t z = 0; z < dgn->size_z; z++) { + for (uint32_t x = 0; x < dgn->size_x; x++) { dungeon_map_update_cell(ctx, x, y, z); } } @@ -93,6 +97,7 @@ void dungeon_map_init(struct dungeon_context *ctx) static void reveal_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z) { + ctx->map->walk_data[dgn_cell_index(ctx->dgn, dgn_x, dgn_y, dgn_z)] = 1; struct texture *dst = &ctx->map->textures[dgn_y]; int x = dgn_x * CS; int y = (ctx->dgn->size_z - dgn_z - 1) * CS; @@ -130,7 +135,7 @@ void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, else gfx_fill(dst, x, y, CS, CS, 182, 72, 0); - const SDL_Color wall_colors[2] = {{255, 255, 255}, {255, 0, 0}}; + const SDL_Color wall_colors[2] = {{255, 255, 255, 255}, {255, 0, 0, 255}}; if (cell->north_wall >= 0) draw_hline(dst, x, y, CS, wall_colors[!!cell->enterable_north]); if (cell->south_wall >= 0) @@ -299,10 +304,119 @@ void dungeon_map_set_walked(int surface, int x, int y, int z, int flag) reveal_cell(ctx, x, y, z); if (x >= 0 && dgn_cell_at(ctx->dgn, x - 1, y, z)->floor < 0) reveal_cell(ctx, x - 1, y, z); - if (x + 1 < ctx->dgn->size_x && dgn_cell_at(ctx->dgn, x + 1, y, z)->floor < 0) + if (x + 1u < ctx->dgn->size_x && dgn_cell_at(ctx->dgn, x + 1, y, z)->floor < 0) reveal_cell(ctx, x + 1, y, z); if (z >= 0 && dgn_cell_at(ctx->dgn, x, y, z - 1)->floor < 0) reveal_cell(ctx, x, y, z - 1); - if (z + 1 < ctx->dgn->size_z && dgn_cell_at(ctx->dgn, x, y, z + 1)->floor < 0) + if (z + 1u < ctx->dgn->size_z && dgn_cell_at(ctx->dgn, x, y, z + 1)->floor < 0) reveal_cell(ctx, x, y, z + 1); } + +/* + * WalkData serialization format (in Rance VI): + * + * struct WalkData { + * int version; // zero + * int nr_maps; + * struct { + * int map_no; + * int size_x, size_y, size_z; + * int cells[size_x * size_y * size_z]; + * } maps[nr_maps]; + * }; + */ + +enum { + WALK_DATA_NOT_FOUND = -1, + WALK_DATA_BROKEN = -2, +}; + +static int find_walk_data(struct page *array, int map_no, struct dgn *dgn) +{ + if (!array) + return WALK_DATA_BROKEN; + if (array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->rank != 1) + VM_ERROR("Not a flat integer array"); + + int ptr = 0; + if (array->nr_vars < 2 || array->values[ptr++].i != 0) + return WALK_DATA_BROKEN; + int nr_maps = array->values[ptr++].i; + + for (int i = 0; i < nr_maps; i++) { + if (array->nr_vars < ptr + 4) + return WALK_DATA_BROKEN; + int no = array->values[ptr].i; + uint32_t size_x = array->values[ptr + 1].i; + uint32_t size_y = array->values[ptr + 2].i; + uint32_t size_z = array->values[ptr + 3].i; + int nr_cells = size_x * size_y * size_z; + if (no == map_no) { + if (size_x != dgn->size_x || size_y != dgn->size_y || size_z != dgn->size_z) + return WALK_DATA_BROKEN; + if (ptr + 4 + nr_cells > array->nr_vars) + return WALK_DATA_BROKEN; + return ptr; + } + ptr += 4 + nr_cells; + } + return WALK_DATA_NOT_FOUND; +} + +bool dungeon_map_load_walk_data(int surface, int map, struct page **page) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->dgn) + return false; + struct page *array = *page; + + int offset = find_walk_data(array, map, ctx->dgn); + if (offset < 0) + return false; + + offset += 4; + int i = 0; + for (uint32_t z = 0; z < ctx->dgn->size_z; z++) { + for (uint32_t y = 0; y < ctx->dgn->size_y; y++) { + for (uint32_t x = 0; x < ctx->dgn->size_x; x++) { + if (array->values[offset].i) + reveal_cell(ctx, x, y, z); + ctx->map->walk_data[i++] = array->values[offset++].i; + } + } + } + + return true; +} + +bool dungeon_map_save_walk_data(int surface, int map, struct page **page) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->dgn) + return false; + struct page *array = *page; + int nr_cells = ctx->dgn->size_x * ctx->dgn->size_y * ctx->dgn->size_z; + + int offset = find_walk_data(array, map, ctx->dgn); + if (offset == WALK_DATA_NOT_FOUND) { + offset = array->nr_vars; + array->values[1].i += 1; + union vm_value dim = { .i = array->nr_vars + 4 + nr_cells }; + *page = array = realloc_array(array, 1, &dim, AIN_ARRAY_INT, 0, false); + } else if (offset == WALK_DATA_BROKEN) { + if (array) + WARNING("Discarding broken walk data"); + union vm_value dim = { .i = 2 + 4 + nr_cells }; + *page = array = realloc_array(array, 1, &dim, AIN_ARRAY_INT, 0, false); + array->values[0].i = 0; + array->values[1].i = 1; + offset = 2; + } + array->values[offset++].i = map; + array->values[offset++].i = ctx->dgn->size_x; + array->values[offset++].i = ctx->dgn->size_y; + array->values[offset++].i = ctx->dgn->size_z; + for (int i = 0; i < nr_cells; i++) + array->values[offset++].i = ctx->map->walk_data[i]; + return true; +} diff --git a/src/hll/DrawDungeon.c b/src/hll/DrawDungeon.c index 8b4a922..86217cf 100644 --- a/src/hll/DrawDungeon.c +++ b/src/hll/DrawDungeon.c @@ -249,8 +249,6 @@ static int DrawDungeon_GetTexSound(int surface, int type, int num) HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, StopTimer, void); HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, RestartTimer, void); -HLL_WARN_UNIMPLEMENTED(0, bool, DrawDungeon, LoadWalkData, int surface, int map, struct page **anWalkData); -HLL_WARN_UNIMPLEMENTED(1, bool, DrawDungeon, SaveWalkData, int surface, int map, struct page **anWalkData); HLL_LIBRARY(DrawDungeon, HLL_EXPORT(Init, DrawDungeon_Init), @@ -369,6 +367,6 @@ HLL_LIBRARY(DrawDungeon, HLL_EXPORT(GetTexSound, DrawDungeon_GetTexSound), HLL_EXPORT(StopTimer, DrawDungeon_StopTimer), HLL_EXPORT(RestartTimer, DrawDungeon_RestartTimer), - HLL_EXPORT(LoadWalkData, DrawDungeon_LoadWalkData), - HLL_EXPORT(SaveWalkData, DrawDungeon_SaveWalkData) + HLL_EXPORT(LoadWalkData, dungeon_map_load_walk_data), + HLL_EXPORT(SaveWalkData, dungeon_map_save_walk_data) );