diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-06-24 16:55:18 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-06-26 20:00:27 +0200 |
| commit | 7c75118429596dcfd86dbefb32e9ae79585c4da0 (patch) | |
| tree | d06478b8e303d0e2729c57b079f481fbcf5b9adf /src/player | |
| parent | f17c76c4007563389188c147d4e1c766039fb686 (diff) | |
| download | rts-7c75118429596dcfd86dbefb32e9ae79585c4da0.tar.gz | |
Revamp gui component
`gui` was tighly coupled to game logic, and could not be extended for
other purposes. Therefore, a generic GUI implementation, loosely
inspired by well-known GUI frameworks such as GTK, is now provided, with
the following properties:
- Does not depend on dynamic or static memory allocation, only automatic
(i.e., stack) memory allocation required.
- Portable among existing implementations.
- Simple to extend.
- Tiny memory footprint.
`gui` is now composed by GUI elements that can be chained to form a tree
structure. This is useful e.g.: to calculate X/Y coordinates for a given
GUI element given its parent(s).
This commit also refactors the older implementation, moving
game-specific logic into `player` and making use of the new component.
Diffstat (limited to 'src/player')
| -rw-r--r-- | src/player/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/player/privinc/human_player_private.h | 18 | ||||
| -rw-r--r-- | src/player/src/human_player.c | 5 | ||||
| -rw-r--r-- | src/player/src/human_player_gui.c | 341 |
4 files changed, 368 insertions, 4 deletions
diff --git a/src/player/CMakeLists.txt b/src/player/CMakeLists.txt index 6d692fe..3d995ee 100644 --- a/src/player/CMakeLists.txt +++ b/src/player/CMakeLists.txt @@ -1,5 +1,9 @@ -add_library(player "src/player.c" "src/human_player.c") -target_include_directories(player PUBLIC "inc") +add_library(player + "src/player.c" + "src/human_player.c" + "src/human_player_gui.c" +) +target_include_directories(player PUBLIC "inc" PRIVATE "privinc") target_link_libraries(player PUBLIC building diff --git a/src/player/privinc/human_player_private.h b/src/player/privinc/human_player_private.h new file mode 100644 index 0000000..5d8a557 --- /dev/null +++ b/src/player/privinc/human_player_private.h @@ -0,0 +1,18 @@ +#ifndef HUMAN_PLAYER_PRIVATE_H +#define HUMAN_PLAYER_PRIVATE_H + +#include <human_player.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +void human_player_gui_update(struct human_player *h); +int human_player_gui_render(const struct human_player *h); + +#ifdef __cplusplus +} +#endif + +#endif /* HUMAN_PLAYER_PRIVATE_H */ diff --git a/src/player/src/human_player.c b/src/player/src/human_player.c index 1b77105..221146d 100644 --- a/src/player/src/human_player.c +++ b/src/player/src/human_player.c @@ -1,4 +1,5 @@ #include <human_player.h> +#include <human_player_private.h> #include <player.h> #include <building.h> #include <camera.h> @@ -595,7 +596,7 @@ void human_player_update(struct human_player *const h, if (p->alive) { - gui_update(h); + human_player_gui_update(h); update_selected(h); update_target(h); peripheral_update(&h->periph); @@ -687,7 +688,7 @@ int human_player_render(const struct human_player *const h, || render_own_units(h) || render_own_buildings(h) || render_resources(h, o->res, o->n_res) - || gui_render(h)) + || human_player_gui_render(h)) return -1; switch (h->periph.common.type) diff --git a/src/player/src/human_player_gui.c b/src/player/src/human_player_gui.c new file mode 100644 index 0000000..93228c8 --- /dev/null +++ b/src/player/src/human_player_gui.c @@ -0,0 +1,341 @@ +#include <building.h> +#include <gfx.h> +#include <human_player.h> +#include <player.h> +#include <unit.h> +#include <gui.h> +#include <gui/bar.h> +#include <gui/label.h> +#include <gui/progress_bar.h> +#include <gui/rounded_rect.h> +#include <inttypes.h> +#include <limits.h> +#include <stddef.h> +#include <stdio.h> +#include <stdint.h> + +enum {X_OFF = 8, Y_OFF = 8, HP_Y = 32}; + +static int draw_hp(const struct instance *const i, const instance_hp max_hp, + struct gui_common *const r) +{ + enum {WIDTH = 64, HEIGHT = 4}; + struct gui_progress_bar pb; + + gui_progress_bar_init(&pb); + pb.common.x = X_OFF; + pb.common.y = HP_Y - 8; + pb.progress = ((unsigned)GUI_PROGRESS_BAR_MAX * i->hp) / max_hp; + pb.fg.g = UCHAR_MAX >> 1; + pb.bg.r = UCHAR_MAX >> 1; + pb.w = WIDTH; + pb.h = HEIGHT; + pb.stp = true; + gui_add_child(r, &pb.common); + + return gui_render(r); +} + +#if 0 +static int draw_miners(const struct resource *const r) +{ + const struct resource_gold *const g = &r->res.gold; + + if (!g->n_miners) + return 0; + + for (size_t i = 0, n = 0; i < sizeof g->miners / sizeof *g->miners; i++) + { + if (g->miners[i]) + { + enum {OFFSET = 112, SZ = 16, GAP = 4}; + + rect_get_or_ret(r, -1); + semitrans_rect_init(r); + r->x = OFFSET + n * (SZ + GAP); + r->y = screen_h - 40; + r->r = r->g = r->b = 127; + r->w = r->h = SZ; + rect_sort(r); + + if (++n >= g->n_miners) + break; + } + } + + return 0; +} +#endif + +void human_player_gui_update(struct human_player *const h) +{ + struct player *const pl = &h->pl; + + for (size_t i = 0; i < sizeof pl->resources / sizeof *pl->resources; i++) + { + if (h->gui_res[i] > pl->resources[i]) + h->gui_res[i]--; + else if (h->gui_res[i] < pl->resources[i]) + h->gui_res[i]++; + } +} + +static int render_sel_single_building(const struct human_player *const h, + const struct sel_instance *const sel, struct gui_common *const r) +{ + const struct building *const b = sel->d.b; + const struct instance *const in = &b->instance; + const instance_hp hp = in->hp, max_hp = building_maxhp(b); + struct gui_label bl; + + gui_label_init(&bl); + bl.common.x = X_OFF; + bl.common.y = Y_OFF; + bl.text = building_str(b); + gui_add_child(r, &bl.common); + + char hp_str[sizeof "65535/65535"]; + + const int rs = snprintf(hp_str, sizeof hp_str, "%u/%u", hp, max_hp); + + if (rs < 0 || rs >= sizeof hp_str) + return -1; + + struct gui_label hpl; + + gui_label_init(&hpl); + hpl.common.x = X_OFF; + hpl.common.y = HP_Y; + hpl.text = hp_str; + gui_add_child(r, &hpl.common); + + return draw_hp(in, max_hp, r); +} + +static int render_sel_single_unit(const struct human_player *const h, + const struct sel_instance *const sel, struct gui_common *const r) +{ + const struct unit *const u = sel->d.u; + const struct instance *const in = &u->instance; + const instance_hp hp = in->hp, max_hp = unit_maxhp(u); + enum {CARRY_X = 96, CARRY_Y = 8}; + + struct gui_label ul; + + gui_label_init(&ul); + ul.common.x = X_OFF; + ul.common.y = Y_OFF; + ul.text = unit_str(u); + gui_add_child(r, &ul.common); + + char hp_str[sizeof "65535/65535"]; + + const int rs = snprintf(hp_str, sizeof hp_str, "%u/%u", hp, max_hp); + + if (rs < 0 || rs >= sizeof hp_str) + return -1; + + struct gui_label hpl; + + gui_label_init(&hpl); + hpl.common.x = X_OFF; + hpl.common.y = HP_Y; + hpl.text = hp_str; + gui_add_child(r, &hpl.common); + + if (unit_can_harvest(u)) + { + const struct unit_harvester *const uh = &u->us.harvester; + + if (uh->carry) + { + char c_str[sizeof "255"]; + const int rs = snprintf(c_str, sizeof c_str, "%hhu", uh->carry); + struct gui_label cl; + + gui_label_init(&cl); + cl.common.x = CARRY_X; + cl.common.y = CARRY_Y; + cl.text = c_str; + gui_add_child(r, &cl.common); + + if (rs < 0 || rs >= sizeof c_str) + return -1; + + return gui_render(r); + } + } + + return draw_hp(in, max_hp, r); +} + +static int render_sel_single_resource(const struct human_player *const h, + const struct sel_instance *const sel, struct gui_common *const r) +{ + const struct resource *const res = sel->d.r; + const struct instance *const in = &res->instance; + const instance_hp hp = in->hp, max_hp = resource_maxhp(res); + struct gui_label rl; + + gui_label_init(&rl); + rl.common.x = X_OFF; + rl.common.y = Y_OFF; + rl.text = resource_str(res); + gui_add_child(r, &rl.common); + + char hp_str[sizeof "65535/65535"]; + const int rs = snprintf(hp_str, sizeof hp_str, "%u/%u", hp, max_hp); + + if (rs < 0 || rs >= sizeof hp_str) + return -1; + + struct gui_label hpl; + + gui_label_init(&hpl); + hpl.common.x = X_OFF; + hpl.common.y = HP_Y; + hpl.text = hp_str; + gui_add_child(r, &hpl.common); + + return draw_hp(in, max_hp, r); +} + +static int render_sel_single(const struct human_player *const h, + struct gui_common *const r) +{ + for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++) + { + const struct sel_instance *const sel = &h->sel[i]; + + if (sel) + { + switch (sel->type) + { + case INSTANCE_TYPE_BUILDING: + return render_sel_single_building(h, sel, r); + + case INSTANCE_TYPE_UNIT: + return render_sel_single_unit(h, sel, r); + + case INSTANCE_TYPE_RESOURCE: + return render_sel_single_resource(h, sel, r); + } + } + } + + /* Unreachable. */ + return -1; +} + +static int render_sel_multiple(const struct human_player *const h, + struct gui_common *const r) +{ + struct gui_label l; + char str[sizeof "4294967295 units selected"]; + const int rs = snprintf(str, sizeof str, "%zu units selected", h->n_sel); + + if (rs < 0 || rs >= sizeof str) + return -1; + + gui_label_init(&l); + l.common.x = X_OFF; + l.common.y = Y_OFF; + l.text = str; + gui_add_child(r, &l.common); + + return gui_render(r); +} + +static int render_sel(const struct human_player *const h) +{ + enum {OFFSET = 60}; + struct gui_rounded_rect r; + + gui_rounded_rect_init(&r); + r.common.x = 16; + r.common.y = screen_h - OFFSET; + r.w = screen_w - (r.common.x * 2); + r.h = OFFSET; + + if (h->n_sel == 1) + return render_sel_single(h, &r.common); + + return render_sel_multiple(h, &r.common); +} + +static int render_top(const struct human_player *const h) +{ + const struct player *const pl = &h->pl; + struct gui_bar b; + + gui_bar_init(&b); + char wood_str[sizeof "Wood=429496729"]; + + { + const int rs = snprintf(wood_str, sizeof wood_str, + "Wood=%" PRIu32, h->gui_res[RESOURCE_TYPE_WOOD]); + + if (rs < 0 || rs >= sizeof wood_str) + return -1; + } + + struct gui_label wl; + + gui_label_init(&wl); + wl.common.x = 16; + wl.common.y = 6; + wl.text = wood_str; + gui_add_child(&b.common, &wl.common); + + char gold_str[sizeof "Gold=429496729"]; + + { + const int rs = snprintf(gold_str, sizeof gold_str, + "Gold=%" PRIu32, h->gui_res[RESOURCE_TYPE_GOLD]); + + if (rs < 0 || rs >= sizeof wood_str) + return -1; + } + + struct gui_label gl; + + gui_label_init(&gl); + gl.common.x = 108; + gl.common.y = 6; + gl.text = gold_str; + gui_add_child(&b.common, &gl.common); + + char pop_str[sizeof "Pop.=255/255"]; + + { + const int rs = snprintf(pop_str, sizeof pop_str, + "Pop.=%hhu/%zu", pl->pop, sizeof pl->units / sizeof *pl->units); + + if (rs < 0 || rs >= sizeof wood_str) + return -1; + } + + struct gui_label popl; + + gui_label_init(&popl); + popl.common.x = 212; + popl.common.y = 6; + popl.text = pop_str; + gui_add_child(&b.common, &popl.common); + + if (gui_render(&b.common)) + return -1; + + return 0; +} + +int human_player_gui_render(const struct human_player *const h) +{ + if (h->top_gui && render_top(h)) + return -1; + + if (h->n_sel && render_sel(h)) + return -1; + + return 0; +} |
