Implement DrawDungeon.{Load,Save}WalkData

Also fixes compiler warnings in map.c.
This commit is contained in:
kichikuou
2021-05-23 14:11:18 +09:00
parent d48c9fb5af
commit fa5ed2db35
5 changed files with 134 additions and 12 deletions
+2
View File
@@ -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)
+3
View File
@@ -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 */
+7 -2
View File
@@ -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)];
}
+120 -6
View File
@@ -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;
}
+2 -4
View File
@@ -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)
);