aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-01-27 12:42:20 +0100
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-01-27 17:38:12 +0100
commit0f21739551b2daf578ad9c5ed3367c8a8ed7b7c9 (patch)
treea0e6bc7e10f9f815fe680fa54f5d912fb460608b
parentdaf6f84ccdf817f7088aa527b4b37c2cca91c052 (diff)
Define terrain rendering and loading
-rw-r--r--src/camera/privinc/camera_private.h4
-rw-r--r--src/container/inc/container.h5
-rw-r--r--src/game/inc/game.h1
-rw-r--r--src/game/src/game.c7
-rw-r--r--src/game/src/res.c40
-rw-r--r--src/menu/src/menu.c3
-rw-r--r--src/terrain/CMakeLists.txt6
-rw-r--r--src/terrain/inc/terrain.h81
-rw-r--r--src/terrain/src/init.c154
-rw-r--r--src/terrain/src/render.c109
-rw-r--r--src/terrain/src/terrain.c132
-rw-r--r--src/terrain/src/update.c20
12 files changed, 412 insertions, 150 deletions
diff --git a/src/camera/privinc/camera_private.h b/src/camera/privinc/camera_private.h
index e83c255..bd78cb0 100644
--- a/src/camera/privinc/camera_private.h
+++ b/src/camera/privinc/camera_private.h
@@ -13,8 +13,8 @@ extern "C"
enum
{
- CAMERA_CURSOR_WIDTH = 20,
- CAMERA_CURSOR_HEIGHT = 20
+ CAMERA_CURSOR_WIDTH = 16,
+ CAMERA_CURSOR_HEIGHT = 16
};
void camera_update_pos(struct camera *cam);
diff --git a/src/container/inc/container.h b/src/container/inc/container.h
index 59ab5fd..31685db 100644
--- a/src/container/inc/container.h
+++ b/src/container/inc/container.h
@@ -26,11 +26,6 @@ struct container
struct sprite *sprite;
struct sound *sound;
} data;
-
- struct container_rt
- {
- struct container *c;
- } *rt;
};
int container_load_ex(const char *path, const struct container *list, size_t n);
diff --git a/src/game/inc/game.h b/src/game/inc/game.h
index 66e5e79..1dfdd58 100644
--- a/src/game/inc/game.h
+++ b/src/game/inc/game.h
@@ -30,6 +30,7 @@ struct game_cfg
size_t n;
union peripheral *p;
+ const char *map;
};
int game_resinit(void);
diff --git a/src/game/src/game.c b/src/game/src/game.c
index 084415c..cbfb648 100644
--- a/src/game/src/game.c
+++ b/src/game/src/game.c
@@ -14,8 +14,11 @@ int game(const struct game_cfg *const cfg)
struct human_player human;
struct terrain_map map;
- terrain_init(&map);
- building_set_alive_cb(terrain_block_update, &map);
+ if (terrain_init(cfg->map, &map))
+ {
+ fprintf(stderr, "%s: terrain_init failed\n", __func__);
+ return -1;
+ }
const struct human_player_cfg hcfg =
{
diff --git a/src/game/src/res.c b/src/game/src/res.c
index bf05bd9..6489d30 100644
--- a/src/game/src/res.c
+++ b/src/game/src/res.c
@@ -12,7 +12,45 @@
#include <unit.h>
#include <stdbool.h>
-static const struct container c[1];
+static const struct container c[] =
+{
+ {
+ .path = "sidewalk",
+ .type = CONTAINER_TYPE_SPRITE,
+ .data.sprite = &terrain_sprites[SIDEWALK]
+ },
+ {
+ .path = "roof1",
+ .type = CONTAINER_TYPE_SPRITE,
+ .data.sprite = &terrain_sprites[ROOF1]
+ },
+ {
+ .path = "roof2",
+ .type = CONTAINER_TYPE_SPRITE,
+ .data.sprite = &terrain_sprites[ROOF2]
+ },
+ {
+ .path = "cursor",
+ .type = CONTAINER_TYPE_SPRITE,
+ .data.sprite = &cursor_sprite
+ },
+ {
+ .path = "btn_left",
+ .type = CONTAINER_TYPE_SPRITE,
+ .data.sprite = &gui_button_sprites[GUI_BUTTON_LEFT]
+ },
+ {
+ .path = "btn_mid",
+ .type = CONTAINER_TYPE_SPRITE,
+ .data.sprite = &gui_button_sprites[GUI_BUTTON_MID]
+ },
+ {
+ .path = "btn_right",
+ .type = CONTAINER_TYPE_SPRITE,
+ .data.sprite = &gui_button_sprites[GUI_BUTTON_RIGHT]
+ },
+};
+
static bool init;
void game_free(void)
diff --git a/src/menu/src/menu.c b/src/menu/src/menu.c
index de1fa57..cc508e7 100644
--- a/src/menu/src/menu.c
+++ b/src/menu/src/menu.c
@@ -74,7 +74,8 @@ int menu(void)
const struct game_cfg gcfg =
{
- .p = &c.p
+ .p = &c.p,
+ .map = "city1.txt"
};
return game(&gcfg);
diff --git a/src/terrain/CMakeLists.txt b/src/terrain/CMakeLists.txt
index 7a0e5bd..18726c8 100644
--- a/src/terrain/CMakeLists.txt
+++ b/src/terrain/CMakeLists.txt
@@ -1,3 +1,7 @@
-add_library(terrain "src/terrain.c")
+add_library(terrain
+ "src/init.c"
+ "src/render.c"
+ "src/update.c"
+)
target_include_directories(terrain PUBLIC "inc")
target_link_libraries(terrain PUBLIC container camera gfx util)
diff --git a/src/terrain/inc/terrain.h b/src/terrain/inc/terrain.h
index d640f17..f6cf37f 100644
--- a/src/terrain/inc/terrain.h
+++ b/src/terrain/inc/terrain.h
@@ -21,26 +21,95 @@ enum
enum terrain_type
{
- TERRAIN_TYPE_GRASS
+ ROOF1_1_NW,
+ ROOF1_1_N,
+ ROOF1_1_NE,
+ ROOF1_2_NW,
+ ROOF1_2_NE,
+ ROOF1_3_NW,
+ ROOF1_3_N,
+ ROOF1_3_NE,
+ ROOF1_4_N,
+
+ ROOF1_1_W,
+ ROOF1_1_C,
+ ROOF1_1_E,
+ ROOF1_2_SW,
+ ROOF1_2_SE,
+ ROOF1_3_SW,
+ ROOF1_3_S,
+ ROOF1_3_SE,
+ ROOF1_4_C,
+
+ ROOF1_1_SW,
+ ROOF1_1_S,
+ ROOF1_1_SE,
+ ROOF1_5_W,
+ ROOF1_5_C,
+ ROOF1_5_E,
+ ROOF1_6,
+ ROOF1_4_S,
+
+ ROOF2_1_NW,
+ ROOF2_1_N,
+ ROOF2_1_NE,
+ ROOF2_2_NW,
+ ROOF2_2_NE,
+ ROOF2_3_NW,
+ ROOF2_3_N,
+ ROOF2_3_NE,
+ ROOF2_4_N,
+
+ ROOF2_1_W,
+ ROOF2_1_C,
+ ROOF2_1_E,
+ ROOF2_2_SW,
+ ROOF2_2_SE,
+ ROOF2_3_SW,
+ ROOF2_3_S,
+ ROOF2_3_SE,
+ ROOF2_4_C,
+
+ ROOF2_1_SW,
+ ROOF2_1_S,
+ ROOF2_1_SE,
+ ROOF2_5_W,
+ ROOF2_5_C,
+ ROOF2_5_E,
+ ROOF2_6,
+ ROOF2_4_S,
+
+ ROOF1_START = ROOF1_1_NW,
+ ROOF1_END = ROOF1_4_S,
+ ROOF2_START = ROOF2_1_NW,
+ ROOF2_END = ROOF2_4_S
};
struct terrain_map
{
struct terrain_tile
{
- enum terrain_type t;
- unsigned char bl;
+ unsigned char t, obj;
} m[MAP_TILES][MAP_TILES];
int nx, ny, last_w, last_h;
};
-void terrain_init(struct terrain_map *map);
+int terrain_init(const char *path, struct terrain_map *map);
void terrain_update(struct terrain_map *map);
int terrain_render(const struct terrain_map *map, const struct camera *cam);
-void terrain_block_update(const struct util_rect *dim, bool alive, void *p);
-extern struct sprite grass_sprite;
+enum
+{
+ SIDEWALK,
+ ROOF1,
+ ROOF2,
+ BUILDING1,
+
+ MAX_TERRAIN_SPRITES
+};
+
+extern struct sprite terrain_sprites[MAX_TERRAIN_SPRITES];
#ifdef __cplusplus
}
diff --git a/src/terrain/src/init.c b/src/terrain/src/init.c
new file mode 100644
index 0000000..88140d8
--- /dev/null
+++ b/src/terrain/src/init.c
@@ -0,0 +1,154 @@
+#include <terrain.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int read_token(FILE *const f, char *const s, const size_t n)
+{
+ uint8_t c;
+ size_t i = 0;
+
+ while (fread(&c, sizeof c, 1, f))
+ {
+ if (isspace(c))
+ {
+ if (i)
+ break;
+ else
+ continue;
+ }
+ else if (!isalnum(c))
+ {
+ fprintf(stderr, "%s: invalid character %" PRIx8 " at offset %ld\n",
+ __func__, c, ftell(f));
+ return -1;
+ }
+ else if (i + 1 >= n)
+ {
+ fprintf(stderr, "%s: exceeded maximum token size %zu\n",
+ __func__, n);
+ return -1;
+ }
+
+ s[i++] = c;
+ }
+
+ if (!i)
+ {
+ fprintf(stderr, "%s: feof=%d, ferror=%d\n", __func__, feof(f),
+ ferror(f));
+ return -1;
+ }
+
+ s[i] = '\0';
+ return 0;
+}
+
+static int read_num(FILE *const f, char *s, const size_t n,
+ unsigned *const out, const int base, const int limit)
+{
+ if (read_token(f, s, n))
+ return -1;
+
+ errno = 0;
+
+ char *end;
+ const unsigned long value = strtoul(s, &end, base);
+
+ if (errno)
+ {
+ fprintf(stderr, "%s: strtoul(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (*end)
+ {
+ fprintf(stderr, "%s: invalid number %s\n", __func__, s);
+ return -1;
+ }
+ else if (value > limit)
+ {
+ fprintf(stderr, "%s: %lu exceeds maximum range %d\n", __func__, value,
+ limit);
+ return -1;
+ }
+
+ *out = value;
+ return 0;
+}
+
+static int read_dec(FILE *const f, unsigned *const out)
+{
+ char s[sizeof "65535"];
+
+ return read_num(f, s, sizeof s, out, 10, UINT16_MAX);
+}
+
+static int read_hex(FILE *const f, unsigned *const out)
+{
+ char s[sizeof "0000"];
+
+ return read_num(f, s, sizeof s, out, 16, UINT16_MAX);
+}
+
+static int read_dimensions(FILE *const f, unsigned *const w, unsigned *const h)
+{
+ if (read_dec(f, w) || read_dec(f, h))
+ return -1;
+ else if (*w > MAP_TILES || *h > MAP_TILES)
+ {
+ fprintf(stderr, "%s: map size (%ux%u) exceeded, max %dx%d\n",
+ __func__, *w, *h, MAP_TILES, MAP_TILES);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int read_map(FILE *const f, const unsigned w, const unsigned h,
+ struct terrain_map *const map)
+{
+ for (unsigned j = 0; j < h; j++)
+ for (unsigned i = 0; i < w; i++)
+ {
+ unsigned tile;
+
+ if (read_hex(f, &tile))
+ return -1;
+
+ map->m[j][i].t = tile;
+ map->m[j][i].obj = tile >> 8;
+ }
+
+ return 0;
+}
+
+int terrain_init(const char *const path, struct terrain_map *const map)
+{
+ int ret = -1;
+ FILE *const f = fopen(path, "rb");
+ unsigned w, h;
+
+ if (!f)
+ {
+ fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno));
+ goto end;
+ }
+ else if (read_dimensions(f, &w, &h) || read_map(f, w, h, map))
+ goto end;
+
+ ret = 0;
+
+end:
+ if (f && fclose(f))
+ {
+ fprintf(stderr, "%s: fclose(3): %s\n", __func__, strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/src/terrain/src/render.c b/src/terrain/src/render.c
new file mode 100644
index 0000000..de354d2
--- /dev/null
+++ b/src/terrain/src/render.c
@@ -0,0 +1,109 @@
+#include <terrain.h>
+#include <camera.h>
+#include <gfx.h>
+#include <util.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct sprite terrain_sprites[MAX_TERRAIN_SPRITES];
+
+static int render_tile(const struct terrain_tile *const t, const short x,
+ const short y)
+{
+ static const struct tile
+ {
+ const struct sprite *s;
+ int start, end;
+ } tiles[] =
+ {
+ {
+ .s = &terrain_sprites[ROOF1],
+ .start = ROOF1_START,
+ .end = ROOF1_END
+ },
+
+ {
+ .s = &terrain_sprites[ROOF2],
+ .start = ROOF2_START,
+ .end = ROOF2_END
+ }
+ };
+
+ for (size_t i = 0; i < sizeof tiles / sizeof *tiles; i++)
+ {
+ const struct tile *const rt = &tiles[i];
+
+ if (t->t >= rt->start && t->t <= rt->end)
+ {
+ sprite_get_or_ret(s, -1);
+
+ if (sprite_clone(rt->s, s))
+ {
+ fprintf(stderr, "%s: sprite_clone failed\n", __func__);
+ return -1;
+ }
+
+ const unsigned char pos = t->t - rt->start;
+ const short tx = pos % TERRAIN_SZ, ty = pos / TERRAIN_SZ;
+
+ s->x = x;
+ s->y = y;
+ s->w = TERRAIN_SZ;
+ s->h = TERRAIN_SZ;
+ s->u = tx * TERRAIN_SZ;
+ s->v = ty * TERRAIN_SZ;
+
+ const int ret = sprite_sort(s);
+
+ if (ret)
+ fprintf(stderr, "%s: sprite_sort failed\n", __func__);
+
+ return ret;
+ }
+ }
+
+ fprintf(stderr, "%s: unknown tile %#hhx\n", __func__, t->t);
+ return -1;
+}
+
+int terrain_render(const struct terrain_map *const map,
+ const struct camera *const cam)
+{
+ const int start_x = abs(cam->x / TERRAIN_SZ),
+ start_y = abs(cam->y / TERRAIN_SZ);
+
+ const int remx = cam->x % TERRAIN_SZ,
+ remy = cam->y % TERRAIN_SZ;
+
+ int nx = map->nx, ny = map->ny;
+
+ if (abs(remx))
+ nx++;
+
+ if (abs(remy))
+ ny++;
+
+ struct m
+ {
+ size_t i;
+ long p;
+ };
+
+ for (struct m x = {.i = start_x, .p = remx};
+ x.i < nx + start_x; x.i++, x.p += TERRAIN_SZ)
+ for (struct m y = {.i = start_y, .p = remy};
+ y.i < ny + start_y; y.i++, y.p += TERRAIN_SZ)
+ {
+ const struct terrain_tile *const t = &map->m[y.i][x.i];
+
+ if (render_tile(t, x.p, y.p))
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/terrain/src/terrain.c b/src/terrain/src/terrain.c
deleted file mode 100644
index 3e70a52..0000000
--- a/src/terrain/src/terrain.c
+++ /dev/null
@@ -1,132 +0,0 @@
-#include <terrain.h>
-#include <camera.h>
-#include <gfx.h>
-#include <util.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-struct sprite grass_sprite;
-
-void terrain_update(struct terrain_map *const map)
-{
- if (map->last_w != screen_w)
- {
- const int extra = !!(screen_w % TERRAIN_SZ);
-
- map->nx = screen_w / TERRAIN_SZ + extra;
- map->last_w = screen_w;
- }
-
- if (map->last_h != screen_h)
- {
- const int extra = !!(screen_h % TERRAIN_SZ);
-
- map->ny = screen_h / TERRAIN_SZ + extra;
- map->last_h = screen_h;
- }
-}
-
-struct block
-{
- size_t x0, x1, y0, y1;
-};
-
-void terrain_block_update(const struct util_rect *const dim, const bool alive,
- void *const p)
-{
- const struct block b =
- {
- .x0 = dim->x / TERRAIN_SZ,
- .x1 = (dim->x + dim->w) / TERRAIN_SZ,
- .y0 = dim->y / TERRAIN_SZ,
- .y1 = (dim->y + dim->h) / TERRAIN_SZ
- };
-
- for (size_t x = b.x0; x <= b.x1; x++)
- for (size_t y = b.y0; y <= b.y1; y++)
- for (size_t i = 0; i < 2; i++)
- for (size_t j = 0; j < 2; j++)
- {
- const struct util_rect sub =
- {
- .x = x * TERRAIN_SZ + i * (TERRAIN_SZ / 2),
- .y = y * TERRAIN_SZ + j * (TERRAIN_SZ / 2),
- .w = TERRAIN_SZ / 2,
- .h = TERRAIN_SZ / 2
- };
-
- if (util_collision(dim, &sub))
- {
- const enum
- {
- NO_SUB = 0,
- UPPER_LEFT = 1 << 0,
- UPPER_RIGHT = 1 << 1,
- LOWER_LEFT = 1 << 2,
- LOWER_RIGHT = 1 << 3,
- ALL_BLOCKS = UPPER_LEFT | UPPER_RIGHT
- | LOWER_LEFT | LOWER_RIGHT
- } sub = 1 << (i + j * 2);
-
- struct terrain_map *const map = p;
- unsigned char *const bl = &map->m[y][x].bl;
-
- *bl = alive ? *bl | sub : *bl & ~sub;
- }
- }
-}
-
-int terrain_render(const struct terrain_map *const map,
- const struct camera *const cam)
-{
- const int start_x = abs(cam->x / TERRAIN_SZ),
- start_y = abs(cam->y / TERRAIN_SZ);
-
- const int remx = cam->x % TERRAIN_SZ,
- remy = cam->y % TERRAIN_SZ;
-
- int nx = map->nx, ny = map->ny;
-
- if (abs(remx))
- nx++;
-
- if (abs(remy))
- ny++;
-
- struct m
- {
- size_t i;
- long p;
- };
-
- for (struct m x = {.i = start_x, .p = remx};
- x.i < nx + start_x; x.i++, x.p += TERRAIN_SZ)
- for (struct m y = {.i = start_y, .p = remy};
- y.i < ny + start_y; y.i++, y.p += TERRAIN_SZ)
- {
- const struct terrain_tile *const t = &map->m[y.i][x.i];
-
- sprite_get_or_ret(s, -1);
-
- switch (t->t)
- {
- case TERRAIN_TYPE_GRASS:
- if (sprite_clone(&grass_sprite, s))
- return -1;
-
- break;
- }
-
- s->x = x.p;
- s->y = y.p;
- sprite_sort(s);
- }
-
- return 0;
-}
-
-void terrain_init(struct terrain_map *const map)
-{
- *map = (const struct terrain_map){0};
-}
diff --git a/src/terrain/src/update.c b/src/terrain/src/update.c
new file mode 100644
index 0000000..8b8eed3
--- /dev/null
+++ b/src/terrain/src/update.c
@@ -0,0 +1,20 @@
+#include <terrain.h>
+
+void terrain_update(struct terrain_map *const map)
+{
+ if (map->last_w != screen_w)
+ {
+ const int extra = !!(screen_w % TERRAIN_SZ);
+
+ map->nx = screen_w / TERRAIN_SZ + extra;
+ map->last_w = screen_w;
+ }
+
+ if (map->last_h != screen_h)
+ {
+ const int extra = !!(screen_h % TERRAIN_SZ);
+
+ map->ny = screen_h / TERRAIN_SZ + extra;
+ map->last_h = screen_h;
+ }
+}