From f7001d91a75204a2ecc33c9a8b93a996865fb418 Mon Sep 17 00:00:00 2001 From: Lizzy Fleckenstein Date: Mon, 24 Jun 2024 00:01:06 +0200 Subject: rewrite map rendering Signed-off-by: Lizzy Fleckenstein --- assets/map_valley.png | Bin 1602 -> 5348 bytes include/content.h | 25 +++++++-- meson.build | 1 + src/client.c | 149 ++++++++++++++++++++++++++++++++++---------------- src/server.c | 49 ++++++++++++++--- 5 files changed, 165 insertions(+), 59 deletions(-) diff --git a/assets/map_valley.png b/assets/map_valley.png index e346088..0ef1214 100644 Binary files a/assets/map_valley.png and b/assets/map_valley.png differ diff --git a/include/content.h b/include/content.h index 9d35dc8..16118f6 100644 --- a/include/content.h +++ b/include/content.h @@ -10,11 +10,11 @@ #include "ser.h" #include "vec.h" -#define SIGHT_RANGE 10 +#define SIGHT_RANGE 15 #define PKT_NODES_MAX (50*50) typedef enum : uint16_t { - N_VALLEY_FLOWER, + N_VALLEY_FLOWER = 0, N_MOUNTAIN_FLOWER, N_BIG_TREE, N_NEEDLE_TREE, @@ -55,11 +55,24 @@ typedef struct { return (color) { (u >> 16) & 0xFF, (u >> 8) & 0xFF, u & 0xFF }; } +typedef enum : uint8_t { + FLOWER_ROSE = 0, + FLOWER_HIBISCUS, + FLOWER_SUNFLOWER, + FLOWER_TULIP, + FLOWER_DANDELION, // im in a field of dandelions... + FLOWER_COUNT, +} flower_type; + +#define ser_flower_type ser_u8 +#define deser_flower_type deser_u8 + typedef struct { bool present; node_type type; int8_t z; // for rocks, indicates rock level color col; + uint8_t variant; } node; [[maybe_unused]] static void ser_node(strbuf *w, node *n) @@ -71,6 +84,7 @@ typedef struct { ser_node_type(w, n->type); ser_i8(w, n->z); ser_color(w, n->col); + ser_u8(w, n->variant); } [[maybe_unused]] static bool deser_node(str *r, node *n) @@ -80,7 +94,8 @@ typedef struct { return deser_node_type(r, &n->type) && deser_i8(r, &n->z) - && deser_color(r, &n->col); + && deser_color(r, &n->col) + && deser_u8(r, &n->variant); } typedef enum : uint8_t { @@ -117,14 +132,14 @@ typedef uint64_t entity_id; #define deser_entity_id deser_u64 typedef enum : uint16_t { - ENTITY_PLAYER + ENTITY_PLAYER = 0, } entity_type; #define ser_entity_type ser_u16 #define deser_entity_type deser_u16 typedef enum : uint8_t { - ENTITY_ADD, // type + ENTITY_ADD = 0, // type ENTITY_REMOVE, ENTITY_MOVE, // x y } entity_cmd; diff --git a/meson.build b/meson.build index 540647c..21f603d 100644 --- a/meson.build +++ b/meson.build @@ -5,6 +5,7 @@ project('silly_game', 'c', default_options: ['warning_level=2']) deps = [ + meson.get_compiler('c').find_library('m'), dependency('endian'), dependency('perlin'), ] diff --git a/src/client.c b/src/client.c index 207dc97..964e91f 100644 --- a/src/client.c +++ b/src/client.c @@ -30,6 +30,9 @@ typedef struct { chunk map; node *map_swap; vec2 player_pos; + char *termbuf_buf; + size_t termbuf_size; + FILE *termbuf; } client; void gfx_alt_buffer(bool enable) @@ -38,79 +41,129 @@ void gfx_alt_buffer(bool enable) printf("\e[?1049h\e[?25l"); else printf("\e[?1049l\e[?25h"); + fflush(stdout); } -void gfx_clear_effects() +void gfx_clear_effects(client *c) { - printf("\e[0m"); + fprintf(c->termbuf, "\e[0m"); } -void gfx_set_color(color c, bool bg) +void gfx_set_color(client *c, color col, bool bg) { - printf("\e[%u;2;%u;%u;%um", bg ? 48 : 38, c.r, c.g, c.b); + fprintf(c->termbuf, "\e[%u;2;%u;%u;%um", bg ? 48 : 38, col.r, col.g, col.b); } -void gfx_clear() +void gfx_clear(client *c) { - printf("\e[2J"); + fprintf(c->termbuf, "\e[2J"); } -void gfx_render_node(client *c, vec2 pos, node *n) +#define C color_from_u32 +#define DISPLAY_BG(X) (node_display) { X, { 0 }, NULL } +#define DISPLAY(...) (node_display) { __VA_ARGS__ } +#define DISPLAY_ERR DISPLAY(C(0xff0000), C(0x000000), " ?") + +typedef struct { + color bg; + color fg; + char *texture; +} node_display; + +bool gfx_rock_bg(node *n, color *c) { - if (!n->present) { - gfx_set_color(color_from_u32(0x555555), true); - gfx_set_color(color_from_u32(0xffffff), false); - printf(" ?"); - return; + switch (n->z) { + case 1: *c = C(0x595959); return true; + case 2: *c = C(0x484848); return true; + case 3: *c = C(0x373737); return true; + default: return false; } +} - gfx_set_color(n->col, true); +color gfx_grass_bg(vec2 pos) +{ + return C(0x00880d); +} - if (vec2_eq(pos, c->player_pos)) { - printf("🥺"); - return; - } +bool gfx_water_wave(vec2 pos) +{ + return true; +} + +node_display gfx_render_node(client *c, vec2 pos, node *n) +{ + if (!n->present) + return DISPLAY(C(0x555555), C(0xffffff), " ?"); - // TODO: persistence switch (n->type) { - case N_GRASS: if (rand() % 10 == 0) { - gfx_set_color(color_from_u32(0x1c571e), false); - printf(" M"); - return; - } break; - case N_WATER: if (rand() % 10 == 0) { - gfx_set_color(color_from_u32(0x0d79de), false); - printf(" ~"); - return; - } break; - case N_SAND: if (rand() % 15 == 0) { - gfx_set_color(color_from_u32(0xe3a112), false); - printf(" ~"); - return; - } break; - default: break; + case N_VALLEY_FLOWER: + case N_MOUNTAIN_FLOWER: { + node_display d = {0}; + if (n->type == N_VALLEY_FLOWER) + d.bg = gfx_grass_bg(pos); + else if (!gfx_rock_bg(n, &d.bg)) + return DISPLAY_ERR; + switch (n->variant) { + case FLOWER_ROSE: d.texture = "🌹"; break; + case FLOWER_HIBISCUS: d.texture = "🌺"; break; + case FLOWER_SUNFLOWER: d.texture = "🌻"; break; + case FLOWER_DANDELION: d.texture = "🌼"; break; + case FLOWER_TULIP: d.texture = "🌷"; break; + default: return DISPLAY_ERR; + } + return d; + } + case N_ROCK: { + node_display d = {0}; + if (!gfx_rock_bg(n, &d.bg)) + return DISPLAY_ERR; + return d; + } + case N_WATER: return DISPLAY(C(0x00c6ff), C(0x0d79de), + n->variant && gfx_water_wave(pos) ? " ~" : NULL); + case N_GRASS: return DISPLAY(gfx_grass_bg(pos), C(0x1c571e), n->variant ? " M" : NULL); + case N_SAND: return DISPLAY(C(0xded485), C(0xe3a112), n->variant ? " ~" : NULL); + case N_NEEDLE_TREE: return DISPLAY_BG(C(0x015100)); + case N_PATH: return DISPLAY_BG(C(0xa09700)); + case N_PLANK: return DISPLAY_BG(C(0x5c3b12)); + case N_BIG_TREE: return DISPLAY_BG(C(0x016300)); + default: return DISPLAY_ERR; } - - printf(" "); } +#undef C +#undef DISPLAY +#undef DISPLAY_ERR void gfx_render(client *c, uint64_t dtime) { - gfx_clear(); - printf("\e[H"); - printf("%.*s\n", PSTR(c->server_motd)); + rewind(c->termbuf); + gfx_clear(c); + fprintf(c->termbuf, "\e[H"); + fprintf(c->termbuf, "%.*s\n", PSTR(c->server_motd)); for (size_t i = 0; i < c->players.len; i++) - printf("%.*s\n", PSTR(c->players.data[i].name)); + fprintf(c->termbuf, "%.*s\n", PSTR(c->players.data[i].name)); for (size_t y = 0; y < c->map.bounds.size.y; y++) { for (size_t x = 0; x < c->map.bounds.size.y; x++) { uvec2 off = UVEC2(x, y); - vec2 p = vec2_add(c->map.bounds.pos, CVEC2(off)); - gfx_render_node(c, p, chunk_index(c->map, off)); + vec2 pos = vec2_add(c->map.bounds.pos, CVEC2(off)); + + node_display dis = gfx_render_node(c, pos, chunk_index(c->map, off)); + gfx_set_color(c, dis.bg, true); + if (vec2_eq(pos, c->player_pos)) { + fprintf(c->termbuf, "🙂"); + } else if (dis.texture) { + gfx_set_color(c, dis.fg, false); + fprintf(c->termbuf, "%s", dis.texture); + } else { + fprintf(c->termbuf, " "); + } } - gfx_clear_effects(); - printf("\n"); + gfx_clear_effects(c); + fprintf(c->termbuf, "\n"); } + fflush(c->termbuf); + fwrite(c->termbuf_buf, 1, ftell(c->termbuf), stdout); fflush(stdout); } @@ -135,8 +188,10 @@ void client_exit(client *c, int ret) peer_free(&c->conn); tcsetattr(STDIN_FILENO, TCSANOW, &c->oldtio); + + fclose(c->termbuf); + free(c->termbuf_buf); gfx_alt_buffer(false); - fflush(stdout); exit(ret); } @@ -314,7 +369,7 @@ int main(int argc, char **argv) str server_name = S("server"); peer_init(&c.conn, socket, &server_name); - setvbuf(stdout, NULL, _IOFBF, 0); + c.termbuf = open_memstream(&c.termbuf_buf, &c.termbuf_size); gfx_alt_buffer(true); SEND_PKT(c.conn, SPKT_HI, @@ -326,7 +381,7 @@ int main(int argc, char **argv) c.map_swap = malloc(c.map.bounds.size.x * c.map.bounds.size.y * sizeof(node)); ticker t; - ticker_init(&t, NANOS/60); + ticker_init(&t, NANOS/30); for (;;) { struct pollfd fds[2]; diff --git a/src/server.c b/src/server.c index 30b5676..974e396 100644 --- a/src/server.c +++ b/src/server.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "array.h" #include "content.h" #include "net.h" @@ -70,30 +72,63 @@ node *map_node(game *g, vec2 p, int8_t z) return NULL; } +bool silly_noise(vec2 pos, uint32_t grid, double sillyness, int seed) +{ + int64_t x = (int64_t) pos.x+INT_MAX+1; + int64_t y = (int64_t) pos.y+INT_MAX+1; + + if ((y/grid) % 2 == 0) + x += grid/2; + + x += round(noise2d(x/grid, y/grid, 0, seed) * sillyness); + y += round(noise2d(y/grid, x/grid, 0, seed) * sillyness); + + return x % grid == 0 && y % grid == 0; +#undef SMOD +} + void map_load_node(map *m, uvec2 v, color col) { uint32_t ucol = color_to_u32(col); + vec2 pos = vec2_add(m->chunk.bounds.pos, CVEC2(v)); + node *n = chunk_index(m->chunk, v); - n->present = ucol != 0xffffff; + *n = (node) { 0 }; + n->present = ucol != 0xffffff; if (!n->present) return; - n->z = 0; - n->col = col; switch (ucol) { - case 0x00880d: n->type = N_GRASS; break; // TODO: color + case 0x00880d: n->type = N_GRASS; + n->variant = silly_noise(pos, 3, 1.0, 0); + break; case 0x595959: n->type = N_ROCK; n->z = 1; break; case 0x484848: n->type = N_ROCK; n->z = 2; break; case 0x373737: n->type = N_ROCK; n->z = 3; break; - case 0x00ffe8: n->type = N_MOUNTAIN_FLOWER; break; + case 0x00ffe8: n->type = N_MOUNTAIN_FLOWER; + n->variant = FLOWER_DANDELION; + n->z = 3; + break; + case 0x001b51: n->type = N_MOUNTAIN_FLOWER; + n->variant = FLOWER_DANDELION; + n->z = 2; + break; case 0xa09700: n->type = N_PATH; break; case 0x015100: n->type = N_NEEDLE_TREE; break; - case 0x00c6ff: n->type = N_WATER; n->z = -1; break; + case 0x00c6ff: n->type = N_WATER; + n->variant = silly_noise(pos, 3, 0.7, 1); + n->z = -1; + break; case 0x5c3b12: n->type = N_PLANK; break; - case 0xff9de2: n->type = N_VALLEY_FLOWER; break; // TODO: color + case 0xff9de2: n->type = N_VALLEY_FLOWER; + n->variant = rand() % FLOWER_COUNT; + break; case 0x016300: n->type = N_BIG_TREE; break; + case 0xfce84e: n->type = N_SAND; + n->variant = silly_noise(pos, 4, 1.0, 1); + break; default: fprintf(stderr, "invalid color in map %.*s at %"PRIu32" %"PRIu32": %06x\n", PSTR(m->name), v.x, v.y, ucol); -- cgit v1.2.3