diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-01-27 12:42:20 +0100 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-01-27 17:38:12 +0100 |
| commit | 0f21739551b2daf578ad9c5ed3367c8a8ed7b7c9 (patch) | |
| tree | a0e6bc7e10f9f815fe680fa54f5d912fb460608b | |
| parent | daf6f84ccdf817f7088aa527b4b37c2cca91c052 (diff) | |
Define terrain rendering and loading
| -rw-r--r-- | src/camera/privinc/camera_private.h | 4 | ||||
| -rw-r--r-- | src/container/inc/container.h | 5 | ||||
| -rw-r--r-- | src/game/inc/game.h | 1 | ||||
| -rw-r--r-- | src/game/src/game.c | 7 | ||||
| -rw-r--r-- | src/game/src/res.c | 40 | ||||
| -rw-r--r-- | src/menu/src/menu.c | 3 | ||||
| -rw-r--r-- | src/terrain/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/terrain/inc/terrain.h | 81 | ||||
| -rw-r--r-- | src/terrain/src/init.c | 154 | ||||
| -rw-r--r-- | src/terrain/src/render.c | 109 | ||||
| -rw-r--r-- | src/terrain/src/terrain.c | 132 | ||||
| -rw-r--r-- | src/terrain/src/update.c | 20 |
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; + } +} |
