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 | aea50395c5dbc5c49ad93384b0182d45d45c6441 (patch) | |
| tree | 6a15906b840244bbd0a4a4745ed8e61d0a87c0e8 /src | |
| parent | aa047f3b95ba4d9074ecadf5227d454cd1e7e7fa (diff) | |
| download | jancity-aea50395c5dbc5c49ad93384b0182d45d45c6441.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')
| -rw-r--r-- | src/button/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/button/inc/button.h | 36 | ||||
| -rw-r--r-- | src/button/src/button.c | 100 | ||||
| -rw-r--r-- | src/game/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/game/src/res.c | 29 | ||||
| -rw-r--r-- | src/gui/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | src/gui/inc/gui.h | 31 | ||||
| -rw-r--r-- | src/gui/inc/gui/bar.h | 39 | ||||
| -rw-r--r-- | src/gui/inc/gui/button.h | 44 | ||||
| -rw-r--r-- | src/gui/inc/gui/label.h | 30 | ||||
| -rw-r--r-- | src/gui/inc/gui/progress_bar.h | 37 | ||||
| -rw-r--r-- | src/gui/inc/gui/rounded_rect.h | 43 | ||||
| -rw-r--r-- | src/gui/privinc/gui_private.h | 17 | ||||
| -rw-r--r-- | src/gui/src/bar.c | 84 | ||||
| -rw-r--r-- | src/gui/src/button.c | 167 | ||||
| -rw-r--r-- | src/gui/src/gui.c | 455 | ||||
| -rw-r--r-- | src/gui/src/label.c | 25 | ||||
| -rw-r--r-- | src/gui/src/progress_bar.c | 93 | ||||
| -rw-r--r-- | src/gui/src/rounded_rect.c | 224 | ||||
| -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 |
23 files changed, 1245 insertions, 598 deletions
diff --git a/src/button/CMakeLists.txt b/src/button/CMakeLists.txt deleted file mode 100644 index 1e6e693..0000000 --- a/src/button/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_library(button "src/button.c") -target_include_directories(button PUBLIC "inc") -target_link_libraries(button PUBLIC gfx PRIVATE font) diff --git a/src/button/inc/button.h b/src/button/inc/button.h deleted file mode 100644 index 062cb07..0000000 --- a/src/button/inc/button.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef BUTTON_H -#define BUTTON_H - -#include <gfx.h> -#include <stdbool.h> - -#ifdef __cplusplus -extern "C" -{ -#endif - -enum -{ - BUTTON_LEFT, - BUTTON_MID, - BUTTON_RIGHT, - - MAX_BUTTON_SPRITES -}; - -struct button -{ - const char *text; - short x, y, w; -}; - -bool button_is_pressed(const struct button *b); -int button_render(const struct button *b); - -extern struct sprite button_sprites[MAX_BUTTON_SPRITES]; - -#ifdef __cplusplus -} -#endif - -#endif /* BUTTON_H */ diff --git a/src/button/src/button.c b/src/button/src/button.c deleted file mode 100644 index 9508d5c..0000000 --- a/src/button/src/button.c +++ /dev/null @@ -1,100 +0,0 @@ -#include <button.h> -#include <font.h> -#include <gfx.h> - -struct sprite button_sprites[MAX_BUTTON_SPRITES]; - -bool button_is_pressed(const struct button *const b) -{ - return false; -} - -static int render_left(const struct button *const b, short *const x) -{ - sprite_get_or_ret(s, -1); - - if (sprite_clone(&button_sprites[BUTTON_LEFT], s)) - return -1; - - s->x = b->x; - s->y = b->y; - sprite_sort(s); - *x = s->x + s->w; - return 0; -} - -static int render_mid(const struct button *const b, short *const x) -{ - const short mid_w = button_sprites[BUTTON_MID].w; - const short lw = button_sprites[BUTTON_LEFT].w; - const short rw = button_sprites[BUTTON_RIGHT].w; - const short w = b->w - lw - rw; - - if (w > 0) - { - const short rem_mid = w > 0 ? w % mid_w : 0; - const short whole_mid = w / mid_w; - const short n_mid = rem_mid ? whole_mid + 1 : whole_mid; - - for (struct - { - size_t i; - short x; - } a = {.i = 0, .x = lw}; - a.i < n_mid; - a.i++, a.x += mid_w) - { - sprite_get_or_ret(m, -1); - - if (sprite_clone(&button_sprites[BUTTON_MID], m)) - return -1; - - m->x = *x; - m->y = b->y; - - if (rem_mid && a.i + 1 == n_mid) - m->w = rem_mid; - else - m->w = mid_w; - - sprite_sort(m); - *x += m->w; - } - } - - return 0; -} - -static int render_right(const struct button *const b, const short *const x) -{ - sprite_get_or_ret(s, -1); - - if (sprite_clone(&button_sprites[BUTTON_RIGHT], s)) - return -1; - - s->x = *x; - s->y = b->y; - sprite_sort(s); - return 0; -} - -static int render_text(const struct button *const b) -{ - const short x = b->x + button_sprites[BUTTON_LEFT].w, - y = b->y + (button_sprites[BUTTON_MID].h >> 2); - - return font_printf(FONT, x, y, "%s", b->text) < 0; -} - -int button_render(const struct button *const b) -{ - short x; - - if (render_left(b, &x) - || render_mid(b, &x) - || render_right(b, &x) - || render_text(b)) - return -1; - - return 0; -} diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index 3eeef87..0c21b0c 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -2,7 +2,6 @@ add_library(game "src/game.c" "src/res.c") target_include_directories(game PUBLIC "inc" PRIVATE "privinc") target_link_libraries(game PRIVATE building - button container font gfx diff --git a/src/game/src/res.c b/src/game/src/res.c index 093fc45..c41af47 100644 --- a/src/game/src/res.c +++ b/src/game/src/res.c @@ -1,10 +1,11 @@ #include <game_private.h> #include <building.h> -#include <button.h> #include <container.h> #include <font.h> #include <gfx.h> -#include <gui.h> +#include <gui/bar.h> +#include <gui/button.h> +#include <gui/rounded_rect.h> #include <resource.h> #include <terrain.h> #include <unit.h> @@ -89,7 +90,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_BAR_LEFT] + .sprite = &gui_bar_sprites[GUI_BAR_LEFT] } }, @@ -98,7 +99,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_BAR_MID] + .sprite = &gui_bar_sprites[GUI_BAR_MID] } }, @@ -107,7 +108,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_BAR_RIGHT] + .sprite = &gui_bar_sprites[GUI_BAR_RIGHT] } }, @@ -116,7 +117,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_SELECTION_UP_LEFT] + .sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_UP_LEFT] } }, @@ -125,7 +126,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_SELECTION_UP_RIGHT] + .sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_UP_RIGHT] } }, @@ -134,7 +135,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_SELECTION_DOWN_LEFT] + .sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_DOWN_LEFT] } }, @@ -143,7 +144,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_SELECTION_DOWN_RIGHT] + .sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_DOWN_RIGHT] } }, @@ -152,7 +153,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_SELECTION_MID] + .sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID] } }, @@ -161,7 +162,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &gui_sprites[GUI_SELECTION_MID_VERT] + .sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID_VERT] } }, @@ -224,7 +225,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &button_sprites[BUTTON_LEFT] + .sprite = &gui_button_sprites[GUI_BUTTON_LEFT] } }, @@ -233,7 +234,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &button_sprites[BUTTON_MID] + .sprite = &gui_button_sprites[GUI_BUTTON_MID] } }, @@ -242,7 +243,7 @@ static const struct container c[] = .type = CONTAINER_TYPE_SPRITE, .data = { - .sprite = &button_sprites[BUTTON_RIGHT] + .sprite = &gui_button_sprites[GUI_BUTTON_RIGHT] } }, }; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 59ee94d..4c5039b 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,3 +1,10 @@ -add_library(gui "src/gui.c") -target_include_directories(gui PUBLIC "inc") -target_link_libraries(gui PUBLIC gfx player PRIVATE font unit building) +add_library(gui + "src/bar.c" + "src/button.c" + "src/gui.c" + "src/label.c" + "src/progress_bar.c" + "src/rounded_rect.c" +) +target_include_directories(gui PUBLIC "inc" PRIVATE "privinc") +target_link_libraries(gui PUBLIC camera gfx peripheral font) diff --git a/src/gui/inc/gui.h b/src/gui/inc/gui.h index 254fd47..ec87f45 100644 --- a/src/gui/inc/gui.h +++ b/src/gui/inc/gui.h @@ -1,33 +1,30 @@ #ifndef GUI_H #define GUI_H +#include <camera.h> #include <gfx.h> -#include <human_player.h> +#include <peripheral.h> #ifdef __cplusplus extern "C" { #endif -enum +struct gui_common { - GUI_BAR_LEFT, - GUI_BAR_MID, - GUI_BAR_RIGHT, - GUI_SELECTION_UP_LEFT, - GUI_SELECTION_UP_RIGHT, - GUI_SELECTION_MID_VERT, - GUI_SELECTION_DOWN_LEFT, - GUI_SELECTION_DOWN_RIGHT, - GUI_SELECTION_MID, - - MAX_GUI_SPRITES + int (*update)(struct gui_common *, const union peripheral *, + const struct camera *); + int (*render)(const struct gui_common *); + short x, y; + bool centered; + struct gui_common *parent, *child, *sibling; }; -void gui_update(struct human_player *h); -int gui_render(const struct human_player *h); - -extern struct sprite gui_sprites[MAX_GUI_SPRITES]; +void gui_add_child(struct gui_common *parent, struct gui_common *child); +void gui_add_sibling(struct gui_common *g, struct gui_common *sibling); +int gui_update(struct gui_common *g, const union peripheral *p, + const struct camera *c); +int gui_render(const struct gui_common *g); #ifdef __cplusplus } diff --git a/src/gui/inc/gui/bar.h b/src/gui/inc/gui/bar.h new file mode 100644 index 0000000..a8000cf --- /dev/null +++ b/src/gui/inc/gui/bar.h @@ -0,0 +1,39 @@ +#ifndef GUI_BAR_H +#define GUI_BAR_H + +#include <gui.h> +#include <gfx.h> +#include <util.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct gui_bar +{ + struct gui_common common; +}; + +UTIL_STATIC_ASSERT(!offsetof(struct gui_bar, common), + "unexpected offset for struct gui_bar"); + +void gui_bar_init(struct gui_bar *b); + +enum +{ + GUI_BAR_LEFT, + GUI_BAR_MID, + GUI_BAR_RIGHT, + + MAX_GUI_BAR_SPRITES +}; + +extern struct sprite gui_bar_sprites[MAX_GUI_BAR_SPRITES]; + +#ifdef __cplusplus +} +#endif + +#endif /* GUI_BAR_H */ diff --git a/src/gui/inc/gui/button.h b/src/gui/inc/gui/button.h new file mode 100644 index 0000000..acfc6c1 --- /dev/null +++ b/src/gui/inc/gui/button.h @@ -0,0 +1,44 @@ +#ifndef GUI_BUTTON_H +#define GUI_BUTTON_H + +#include <gfx.h> +#include <gui.h> +#include <gui/label.h> +#include <util.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct gui_button +{ + struct gui_common common; + struct gui_label label; + short w; + void *arg; + void (*on_pressed)(void *); +}; + +UTIL_STATIC_ASSERT(!offsetof(struct gui_button, common), + "unexpected offset for struct gui_button"); + +void gui_button_init(struct gui_button *b); + +enum +{ + GUI_BUTTON_LEFT, + GUI_BUTTON_MID, + GUI_BUTTON_RIGHT, + + MAX_GUI_BUTTON_SPRITES +}; + +extern struct sprite gui_button_sprites[MAX_GUI_BUTTON_SPRITES]; + +#ifdef __cplusplus +} +#endif + +#endif /* GUI_BUTTON_H */ diff --git a/src/gui/inc/gui/label.h b/src/gui/inc/gui/label.h new file mode 100644 index 0000000..7a30624 --- /dev/null +++ b/src/gui/inc/gui/label.h @@ -0,0 +1,30 @@ +#ifndef GUI_LABEL_H +#define GUI_LABEL_H + +#include <gui.h> +#include <font.h> +#include <util.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct gui_label +{ + struct gui_common common; + enum font font; + const char *text; +}; + +UTIL_STATIC_ASSERT(!offsetof(struct gui_label, common), + "unexpected offset for struct gui_label"); + +void gui_label_init(struct gui_label *l); + +#ifdef __cplusplus +} +#endif + +#endif /* GUI_LABEL_H */ diff --git a/src/gui/inc/gui/progress_bar.h b/src/gui/inc/gui/progress_bar.h new file mode 100644 index 0000000..4fa5129 --- /dev/null +++ b/src/gui/inc/gui/progress_bar.h @@ -0,0 +1,37 @@ +#ifndef GUI_PROGRESS_BAR_H +#define GUI_PROGRESS_BAR_H + +#include <gui.h> +#include <util.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum {GUI_PROGRESS_BAR_MAX = 255}; + +struct gui_progress_bar +{ + struct gui_common common; + short w, h; + bool stp; + unsigned char progress; + struct + { + unsigned char r, g, b; + } fg, bg; +}; + +UTIL_STATIC_ASSERT(!offsetof(struct gui_progress_bar, common), + "unexpected offset for struct gui_progress_bar"); + +void gui_progress_bar_init(struct gui_progress_bar *pb); + +#ifdef __cplusplus +} +#endif + +#endif /* GUI_PROGRESS_BAR_H */ diff --git a/src/gui/inc/gui/rounded_rect.h b/src/gui/inc/gui/rounded_rect.h new file mode 100644 index 0000000..2d5a273 --- /dev/null +++ b/src/gui/inc/gui/rounded_rect.h @@ -0,0 +1,43 @@ +#ifndef GUI_ROUNDED_RECT_H +#define GUI_ROUNDED_RECT_H + +#include <gui.h> +#include <gfx.h> +#include <util.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct gui_rounded_rect +{ + struct gui_common common; + unsigned short w, h; +}; + +UTIL_STATIC_ASSERT(!offsetof(struct gui_rounded_rect, common), + "unexpected offset for struct gui_rounded_rect"); + +void gui_rounded_rect_init(struct gui_rounded_rect *l); + +enum +{ + GUI_ROUNDED_RECT_UP_LEFT, + GUI_ROUNDED_RECT_UP_RIGHT, + GUI_ROUNDED_RECT_MID_VERT, + GUI_ROUNDED_RECT_DOWN_LEFT, + GUI_ROUNDED_RECT_DOWN_RIGHT, + GUI_ROUNDED_RECT_MID, + + MAX_GUI_ROUNDED_RECT_SPRITES +}; + +extern struct sprite gui_rounded_rect_sprites[MAX_GUI_ROUNDED_RECT_SPRITES]; + +#ifdef __cplusplus +} +#endif + +#endif /* GUI_ROUNDED_RECT_H */ diff --git a/src/gui/privinc/gui_private.h b/src/gui/privinc/gui_private.h new file mode 100644 index 0000000..af18dcd --- /dev/null +++ b/src/gui/privinc/gui_private.h @@ -0,0 +1,17 @@ +#ifndef GUI_PRIVATE_H +#define GUI_PRIVATE_H + +#include <gui.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +void gui_coords(const struct gui_common *g, short *x, short *y); + +#ifdef __cplusplus +} +#endif + +#endif /* GUI_PRIVATE_H */ diff --git a/src/gui/src/bar.c b/src/gui/src/bar.c new file mode 100644 index 0000000..708e4d1 --- /dev/null +++ b/src/gui/src/bar.c @@ -0,0 +1,84 @@ +#include <gui/bar.h> +#include <gui.h> +#include <gfx.h> + +struct sprite gui_bar_sprites[MAX_GUI_BAR_SPRITES]; + +static int render_topleft(void) +{ + sprite_get_or_ret(s, -1); + + if (sprite_clone(&gui_bar_sprites[GUI_BAR_LEFT], s)) + return -1; + + sprite_sort(s); + return 0; +} + +static int render_topright(void) +{ + sprite_get_or_ret(s, -1); + + if (sprite_clone(&gui_bar_sprites[GUI_BAR_RIGHT], s)) + return -1; + + s->x = screen_w - s->w; + sprite_sort(s); + return 0; +} + +static int render_topmid(void) +{ + const uint16_t mid_w = gui_bar_sprites[GUI_BAR_MID].w; + const uint16_t lw = gui_bar_sprites[GUI_BAR_LEFT].w; + const size_t w = screen_w - lw - lw; + const size_t rem_mid = w % mid_w; + const size_t whole_mid = w / mid_w; + const size_t n_mid = rem_mid ? whole_mid + 1 : whole_mid; + + for (struct + { + size_t i; + short x; + } a = {.i = 0, .x = lw}; + a.i < n_mid; + a.i++, a.x += mid_w) + { + sprite_get_or_ret(m, -1); + + if (sprite_clone(&gui_bar_sprites[GUI_BAR_MID], m)) + return -1; + + m->x = a.x; + + if (rem_mid && a.i + 1 == n_mid) + m->w = rem_mid; + else + m->w = mid_w; + + sprite_sort(m); + } + + return 0; +} + +static int render(const struct gui_common *const g) +{ + if (render_topleft() + || render_topright() + || render_topmid()) + return -1; + + return 0; +} + +void gui_bar_init(struct gui_bar *const b) +{ + *b = (const struct gui_bar) + { + .common = + { + .render = render + } + }; +} diff --git a/src/gui/src/button.c b/src/gui/src/button.c new file mode 100644 index 0000000..eed744c --- /dev/null +++ b/src/gui/src/button.c @@ -0,0 +1,167 @@ +#include <gui.h> +#include <gui/button.h> +#include <gui_private.h> +#include <camera.h> +#include <gfx.h> +#include <mouse.h> +#include <pad.h> +#include <peripheral.h> + +struct sprite gui_button_sprites[MAX_GUI_BUTTON_SPRITES]; + +/* Alias for readability. */ +static const struct sprite *const refs = gui_button_sprites; + +static int render_left(const struct gui_button *const b, + short *const x, const short y) +{ + sprite_get_or_ret(s, -1); + + if (sprite_clone(&refs[GUI_BUTTON_LEFT], s)) + return -1; + + s->x = *x; + s->y = y; + sprite_sort(s); + *x = s->x + s->w; + return 0; +} + +static int render_mid(const struct gui_button *const b, + short *const x, const short y) +{ + const short mid_w = refs[GUI_BUTTON_MID].w; + const short lw = refs[GUI_BUTTON_LEFT].w; + const short rw = refs[GUI_BUTTON_RIGHT].w; + const short w = b->w - lw - rw; + + if (w > 0) + { + const short rem_mid = w > 0 ? w % mid_w : 0; + const short whole_mid = w / mid_w; + const short n_mid = rem_mid ? whole_mid + 1 : whole_mid; + + for (struct + { + size_t i; + short x; + } a = {.x = lw}; + a.i < n_mid; + a.i++, a.x += mid_w) + { + sprite_get_or_ret(m, -1); + + if (sprite_clone(&refs[GUI_BUTTON_MID], m)) + return -1; + + m->x = *x; + m->y = y; + + if (rem_mid && a.i + 1 == n_mid) + m->w = rem_mid; + else + m->w = mid_w; + + sprite_sort(m); + *x += m->w; + } + } + + return 0; +} + +static int render_right(const struct gui_button *const b, + const short x, const short y) +{ + sprite_get_or_ret(s, -1); + + if (sprite_clone(&refs[GUI_BUTTON_RIGHT], s)) + return -1; + + s->x = x; + s->y = y; + sprite_sort(s); + return 0; +} + +static int render(const struct gui_common *const g) +{ + const struct gui_button *const b = (const struct gui_button *)g; + short x, y; + + gui_coords(&b->common, &x, &y); + + if (render_left(b, &x, y) + || render_mid(b, &x, y) + || render_right(b, x, y)) + return -1; + + return 0; +} + +static bool pressed(const struct gui_button *const b, + const union peripheral *const p, + const struct camera *const cam) +{ + bool check = false; + + switch (p->common.type) + { + case PERIPHERAL_TYPE_PAD: + check = pad_pressed(&p->pad.pad, PAD_KEY_A); + break; + + case PERIPHERAL_TYPE_KEYBOARD_MOUSE: + check = mouse_justreleased(&p->kbm.mouse, MOUSE_BUTTON_LEFT); + break; + + case PERIPHERAL_TYPE_TOUCH: + check = mouse_justpressed(&p->kbm.mouse, MOUSE_BUTTON_LEFT); + break; + } + + if (check) + { + short x, y; + + gui_coords(&b->common, &x, &y); + + const struct util_rect d = + { + .x = x, + .y = y, + .w = b->w, + .h = refs[GUI_BUTTON_LEFT].h + }; + + return cursor_collision(cam, &d); + } + + return false; +} + +static int update(struct gui_common *const g, + const union peripheral *const p, const struct camera *const c) +{ + struct gui_button *const b = (struct gui_button *)g; + + if (pressed(b, p, c) && b->on_pressed) + b->on_pressed(b->arg); + + return 0; +} + +void gui_button_init(struct gui_button *const b) +{ + *b = (const struct gui_button) + { + .common = + { + .update = update, + .render = render + } + }; + + gui_label_init(&b->label); + gui_add_child(&b->common, &b->label.common); +} diff --git a/src/gui/src/gui.c b/src/gui/src/gui.c index 187e0b4..044820a 100644 --- a/src/gui/src/gui.c +++ b/src/gui/src/gui.c @@ -1,452 +1,67 @@ #include <gui.h> -#include <building.h> -#include <font.h> -#include <gfx.h> -#include <human_player.h> -#include <player.h> -#include <unit.h> -#include <limits.h> -#include <stddef.h> -struct sprite gui_sprites[MAX_GUI_SPRITES]; - -static int render_topleft(void) -{ - sprite_get_or_ret(s, -1); - - if (sprite_clone(&gui_sprites[GUI_BAR_LEFT], s)) - return -1; - - sprite_sort(s); - return 0; -} - -static int render_topright(void) -{ - sprite_get_or_ret(s, -1); - - if (sprite_clone(&gui_sprites[GUI_BAR_RIGHT], s)) - return -1; - - s->x = screen_w - s->w; - sprite_sort(s); - return 0; -} - -static int render_topmid(void) -{ - const uint16_t mid_w = gui_sprites[GUI_BAR_MID].w; - const uint16_t lw = gui_sprites[GUI_BAR_LEFT].w; - const size_t w = screen_w - lw - lw; - const size_t rem_mid = w % mid_w; - const size_t whole_mid = w / mid_w; - const size_t n_mid = rem_mid ? whole_mid + 1 : whole_mid; - - for (struct - { - size_t i; - short x; - } a = {.i = 0, .x = lw}; - a.i < n_mid; - a.i++, a.x += mid_w) - { - sprite_get_or_ret(m, -1); - - if (sprite_clone(&gui_sprites[GUI_BAR_MID], m)) - return -1; - - m->x = a.x; - - if (rem_mid && a.i + 1 == n_mid) - m->w = rem_mid; - else - m->w = mid_w; - - sprite_sort(m); - } - - return 0; -} - -static int render_top(void) -{ - if (render_topleft() - || render_topright() - || render_topmid()) - return -1; - - return 0; -} - -static int render_seltop(struct sprite *const up) -{ - enum {OFFSET = 48}; - - if (sprite_clone(&gui_sprites[GUI_SELECTION_UP_LEFT], up)) - return -1; - - up->y = screen_h - up->h - OFFSET; - up->x = 16; - sprite_sort(up); - - sprite_get_or_ret(right, -1); - - if (sprite_clone(&gui_sprites[GUI_SELECTION_UP_RIGHT], right)) - return -1; - - right->x = screen_w - right->w - up->x; - right->y = up->y; - sprite_sort(right); - return 0; -} - -static int render_selvert(const struct sprite *const up, const short x) +void gui_coords(const struct gui_common *const g, short *const x, + short *const y) { - const uint16_t vert_h = gui_sprites[GUI_SELECTION_MID_VERT].h; - const size_t h = screen_h - up->y - up->h - up->h; - const size_t rem_vert = h % vert_h; - const size_t whole_vert = h / vert_h; - const size_t n_vert = rem_vert ? whole_vert + 1 : whole_vert; + *x = g->x; + *y = g->y; - for (struct - { - size_t i; - short y; - } a = {.i = 0, .y = up->y + up->h}; - a.i < n_vert; - a.i++, a.y += vert_h) + for (const struct gui_common *p = g->parent; p; p = p->parent) { - sprite_get_or_ret(v, -1); - - if (sprite_clone(&gui_sprites[GUI_SELECTION_MID_VERT], v)) - return -1; - - v->y = a.y; - v->x = x; - - if (rem_vert && a.i + 1 == n_vert) - v->h = rem_vert; - else - v->h = vert_h; - - sprite_sort(v); + *x += p->x; + *y += p->y; } - - return 0; -} - -static int render_selvertleft(const struct sprite *const up) -{ - return render_selvert(up, up->x); } -static int render_selvertright(const struct sprite *const up) +void gui_add_sibling(struct gui_common *const g, + struct gui_common *const s) { - return render_selvert(up, screen_w - gui_sprites[GUI_SELECTION_MID_VERT].w - up->x); -} - -static int render_seldown(const struct sprite *const up) -{ - { - sprite_get_or_ret(left, -1); - - if (sprite_clone(&gui_sprites[GUI_SELECTION_DOWN_LEFT], left)) - return -1; - - left->x = up->x; - left->y = screen_h - left->h; - sprite_sort(left); - } - - { - sprite_get_or_ret(right, -1); - - if (sprite_clone(&gui_sprites[GUI_SELECTION_DOWN_RIGHT], right)) - return -1; - - right->x = screen_w - right->w - up->x; - right->y = screen_h - right->h; - sprite_sort(right); - } - - return 0; -} - -static int render_selrect(const struct sprite *const up) -{ - rect_get_or_ret(sel, -1); - semitrans_rect_init(sel); - - const struct sprite *const mid = &gui_sprites[GUI_SELECTION_MID], - *const vert = &gui_sprites[GUI_SELECTION_MID_VERT]; - sel->x = up->x + vert->w; - sel->y = up->y + mid->h; - sel->w = screen_w - sel->x - vert->w - up->x; - sel->h = screen_h - sel->y - mid->h; - sel->r = 72; - sel->g = 66; - sel->b = 56; - rect_sort(sel); - return 0; -} - -static int render_selmid(const struct sprite *const up, const short y) -{ - const uint16_t mid_w = gui_sprites[GUI_SELECTION_MID].w; - const size_t w = screen_w - up->x - up->x - up->w - up->w; - const size_t rem_mid = w % mid_w; - const size_t whole_mid = w / mid_w; - const size_t n_mid = rem_mid ? whole_mid + 1 : whole_mid; - - for (struct + for (struct gui_common *c = g; c; c = c->sibling) + if (!c->sibling) { - size_t i; - short x; - } a = {.i = 0, .x = up->x + up->w}; - a.i < n_mid; - a.i++, a.x += mid_w) - { - sprite_get_or_ret(m, -1); - *m = gui_sprites[GUI_SELECTION_MID]; - m->x = a.x; - m->y = y; - - if (rem_mid && a.i + 1 == n_mid) - m->w = rem_mid; - else - m->w = mid_w; - - sprite_sort(m); - } - - return 0; + c->sibling = s; + break; + } } -static int render_selmidtop(const struct sprite *const up) +void gui_add_child(struct gui_common *const p, + struct gui_common *const c) { - return render_selmid(up, up->y); -} + if (p->child) + gui_add_sibling(p->child, c); + else + p->child = c; -static int render_selmiddown(const struct sprite *const up) -{ - return render_selmid(up, screen_h - gui_sprites[GUI_SELECTION_MID].h); + c->parent = p; } -static int render_topmenu(const struct human_player *const h) +int gui_update(struct gui_common *const g, const union peripheral *const p, + const struct camera *const c) { - const struct player *const pl = &h->pl; - - if (render_top() - || (font_printf(FONT, 16, 6, "Wood=%u", - h->gui_res[RESOURCE_TYPE_WOOD]) < 0) - || (font_printf(FONT, 108, 6, "Gold=%u", - h->gui_res[RESOURCE_TYPE_GOLD]) < 0) - || (font_printf(FONT, 212, 6, "Pop.=%u/%zu", - pl->pop, sizeof pl->units / sizeof *pl->units) < 0)) + if (g->update && g->update(g, p, c)) return -1; - return 0; -} - -static int draw_hp(const struct instance *const i, const short x, const short y, - const instance_hp max_hp) -{ - enum {OFFSET = 4, WIDTH = 64, HEIGHT = 4}; - const short ry = y - OFFSET - HEIGHT; - - if (ry < 0) - return 0; - - const short gw = (WIDTH * i->hp) / max_hp; - - { - rect_get_or_ret(gr, -1); - semitrans_rect_init(gr); - gr->x = x; - gr->y = ry; - gr->w = gw; - gr->h = HEIGHT; - gr->g = UCHAR_MAX >> 1; - rect_sort(gr); - } - - { - rect_get_or_ret(rr, -1); - semitrans_rect_init(rr); - rr->x = x + gw; - rr->y = ry; - rr->w = WIDTH - gw; - rr->h = HEIGHT; - rr->r = UCHAR_MAX >> 1; - rect_sort(rr); - } - - return 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; -} - -static int draw_carry(const enum font f, const short x, const short y, - const struct unit *const u) -{ - if (unit_can_harvest(u)) - { - const struct unit_harvester *const uh = &u->us.harvester; + if (g->child && gui_update(g->child, p, c)) + return -1; - if (uh->carry) - return font_printf(f, x, y, "%d", uh->carry) < 0; - } + for (struct gui_common *s = g->sibling; s; s = s->sibling) + if (gui_update(s, p, c)) + return -1; return 0; } -static int render_selsingle(const struct sprite *const up, - const struct human_player *const h) -{ - enum {X_OFF = 8, Y_OFF = 8, HP_Y = 32}; - const short x = up->x + X_OFF, y = up->y + Y_OFF; - - 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: - { - 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); - - if ((font_printf(FONT, x, y, "%s", building_str(b)) < 0) - || (font_printf(FONT, x, y + HP_Y, "%u/%u", hp, max_hp) < 0) - || draw_hp(in, x, y + HP_Y - 4, max_hp)) - return -1; - - return 0; - } - break; - - case INSTANCE_TYPE_UNIT: - { - 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}; - - if ((font_printf(FONT, x, y, "%s", unit_str(u)) < 0) - || (font_printf(FONT, x, y + HP_Y, "%u/%u", hp, max_hp) < 0) - || draw_carry(FONT, x + CARRY_X, y, u) - || draw_hp(in, x, y + HP_Y - 4, max_hp)) - return -1; - - return 0; - } - break; - - case INSTANCE_TYPE_RESOURCE: - { - const struct resource *const r = sel->d.r; - const struct instance *const in = &r->instance; - const instance_hp hp = in->hp, max_hp = resource_maxhp(r); - - if ((font_printf(FONT, x, y, "%s", resource_str(r)) < 0) - || (font_printf(FONT, x, y + HP_Y, "%u/%u", hp, max_hp) < 0) - || draw_hp(in, x, y + HP_Y - 4, max_hp) - || (r->type == RESOURCE_TYPE_GOLD && draw_miners(r))) - return -1; - - return 0; - } - break; - } - - return 0; - } - } - - /* Unreachable. */ - return -1; -} - -static int render_selinfo(const struct sprite *const up, - const struct human_player *const h) +int gui_render(const struct gui_common *const g) { - enum {X_OFF = 8, Y_OFF = 8}; - - if (h->n_sel == 1) - return render_selsingle(up, h); - else if (font_printf(FONT, up->x + X_OFF, up->y + Y_OFF, - "%u units selected", h->n_sel) < 0) + if (g->render && g->render(g)) return -1; - return 0; -} - -static int render_sel(const struct human_player *const h) -{ - sprite_get_or_ret(up, -1); - - if (render_seltop(up) - || render_selrect(up) - || render_selmidtop(up) - || render_selmiddown(up) - || render_selvertleft(up) - || render_selvertright(up) - || render_seldown(up) - || render_selinfo(up, h)) + if (g->child && gui_render(g->child)) return -1; - return 0; -} - -void 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]++; - } -} - -int gui_render(const struct human_player *const h) -{ - if ((h->top_gui && render_topmenu(h)) - || (h->n_sel && render_sel(h))) - return -1; + for (struct gui_common *s = g->sibling; s; s = s->sibling) + if (gui_render(s)) + return -1; return 0; } diff --git a/src/gui/src/label.c b/src/gui/src/label.c new file mode 100644 index 0000000..2a24d08 --- /dev/null +++ b/src/gui/src/label.c @@ -0,0 +1,25 @@ +#include <gui/label.h> +#include <gui.h> +#include <gui_private.h> +#include <font.h> + +static int render(const struct gui_common *const g) +{ + const struct gui_label *const l = (const struct gui_label *)g; + short x, y; + + gui_coords(g, &x, &y); + + return l->text ? font_puts(l->font, x, y, l->text) : -1; +} + +void gui_label_init(struct gui_label *const l) +{ + *l = (const struct gui_label) + { + .common = + { + .render = render + } + }; +} diff --git a/src/gui/src/progress_bar.c b/src/gui/src/progress_bar.c new file mode 100644 index 0000000..b836b4d --- /dev/null +++ b/src/gui/src/progress_bar.c @@ -0,0 +1,93 @@ +#include <gui/progress_bar.h> +#include <gui.h> +#include <gui_private.h> +#include <gfx.h> + +static short progress_width(const struct gui_progress_bar *const pb) +{ + return pb->progress ? ((short)pb->progress * pb->w) + / (short)GUI_PROGRESS_BAR_MAX : 0; +} + +static void set_fg_params(const struct gui_progress_bar *const pb, + struct rect *const r, const short x, const short y) +{ + r->x = x; + r->y = y; + r->w = progress_width(pb); + r->h = pb->h; + r->r = pb->fg.r; + r->g = pb->fg.g; + r->b = pb->fg.b; +} + +static void set_bg_params(const struct gui_progress_bar *const pb, + struct rect *const r, const short x, const short y) +{ + const short w = progress_width(pb); + + r->x = x + w; + r->y = y; + r->w = pb->w - w; + r->h = pb->h; + r->r = pb->bg.r; + r->g = pb->bg.g; + r->b = pb->bg.b; +} + +static int render_common(const struct gui_progress_bar *const pb, + const short x, const short y, void (*init)(struct rect *)) +{ + { + rect_get_or_ret(fg, -1); + init(fg); + set_fg_params(pb, fg, x, y); + rect_sort(fg); + } + + { + rect_get_or_ret(bg, -1); + init(bg); + set_bg_params(pb, bg, x, y); + rect_sort(bg); + } + + return 0; +} + +static int render_op(const struct gui_progress_bar *const pb, + const short x, const short y) +{ + return render_common(pb, x, y, rect_init); +} + +static int render_stp(const struct gui_progress_bar *const pb, + const short x, const short y) +{ + return render_common(pb, x, y, semitrans_rect_init); +} + +static int render(const struct gui_common *const g) +{ + const struct gui_progress_bar *const pb = + (const struct gui_progress_bar *)g; + short x, y; + + gui_coords(&pb->common, &x, &y); + + if (pb->stp) + return render_stp(pb, x, y); + + return render_op(pb, x, y); +} + +void gui_progress_bar_init(struct gui_progress_bar *const pb) +{ + *pb = (const struct gui_progress_bar) + { + .common = + { + .render = render + } + }; +} diff --git a/src/gui/src/rounded_rect.c b/src/gui/src/rounded_rect.c new file mode 100644 index 0000000..1ad0902 --- /dev/null +++ b/src/gui/src/rounded_rect.c @@ -0,0 +1,224 @@ +#include <gui/rounded_rect.h> +#include <gui.h> +#include <gui_private.h> +#include <gfx.h> + +struct sprite gui_rounded_rect_sprites[MAX_GUI_ROUNDED_RECT_SPRITES]; + +/* Alias for readability. */ +static const struct sprite *const refs = gui_rounded_rect_sprites; + +static int render_top(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + { + sprite_get_or_ret(left, -1); + + if (sprite_clone(&refs[GUI_ROUNDED_RECT_UP_LEFT], left)) + return -1; + + left->x = x; + left->y = y; + sprite_sort(left); + } + + { + sprite_get_or_ret(right, -1); + + const struct sprite *const ref = + &refs[GUI_ROUNDED_RECT_UP_RIGHT]; + + if (sprite_clone(ref, right)) + return -1; + + right->x = x + r->w - ref->w; + right->y = y; + sprite_sort(right); + } + + return 0; +} + +static int render_vert(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + const struct sprite *const vert = &refs[GUI_ROUNDED_RECT_MID_VERT]; + const short top_h = refs[GUI_ROUNDED_RECT_UP_LEFT].h, + h = r->h - (top_h * 2), + rem_vert = h % vert->h, + whole_vert = h / vert->h, + n_vert = rem_vert ? whole_vert + 1 : whole_vert; + + for (struct + { + size_t i; + short y; + } a = {.y = y + top_h}; a.i < n_vert; a.i++, a.y += vert->h) + { + sprite_get_or_ret(v, -1); + + if (sprite_clone(vert, v)) + return -1; + + v->y = a.y; + v->x = x; + + if (rem_vert && a.i + 1 == n_vert) + v->h = rem_vert; + else + v->h = vert->h; + + sprite_sort(v); + } + + return 0; +} + +static int render_vertleft(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + return render_vert(r, x, y); +} + +static int render_vertright(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + const struct sprite *const ref = &refs[GUI_ROUNDED_RECT_MID_VERT]; + + return render_vert(r, x + r->w - ref->w, y); +} + +static int render_down(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + { + const struct sprite *const ref = &refs[GUI_ROUNDED_RECT_DOWN_LEFT]; + sprite_get_or_ret(left, -1); + + if (sprite_clone(ref, left)) + return -1; + + left->x = x; + left->y = y + r->h - ref->h; + sprite_sort(left); + } + + { + const struct sprite *const ref = &refs[GUI_ROUNDED_RECT_DOWN_RIGHT]; + sprite_get_or_ret(right, -1); + + if (sprite_clone(ref, right)) + return -1; + + right->x = x + r->w - ref->w; + right->y = y + r->h - ref->h; + sprite_sort(right); + } + + return 0; +} + +static int render_rect(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + rect_get_or_ret(sel, -1); + semitrans_rect_init(sel); + + const struct sprite *const mid = &refs[GUI_ROUNDED_RECT_MID], + *const vert = &refs[GUI_ROUNDED_RECT_MID_VERT]; + + sel->x = x + vert->w; + sel->y = y + mid->h; + sel->w = r->w - (vert->w * 2); + sel->h = r->h - (mid->h * 2); + sel->r = 72; + sel->g = 66; + sel->b = 56; + rect_sort(sel); + return 0; +} + +static int render_mid(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + const short mid_w = refs[GUI_ROUNDED_RECT_MID].w, + top_w = refs[GUI_ROUNDED_RECT_UP_LEFT].w, + w = r->w - (top_w * 2), + rem_mid = w % mid_w, + whole_mid = w / mid_w, + n_mid = rem_mid ? whole_mid + 1 : whole_mid; + + for (struct + { + size_t i; + short x; + } a = {.x = x + top_w}; a.i < n_mid; a.i++, a.x += mid_w) + { + sprite_get_or_ret(m, -1); + + if (sprite_clone(&refs[GUI_ROUNDED_RECT_MID], m)) + return -1; + + m->x = a.x; + m->y = y; + + if (rem_mid && a.i + 1 == n_mid) + m->w = rem_mid; + else + m->w = mid_w; + + sprite_sort(m); + } + + return 0; +} + +static int render_midtop(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + return render_mid(r, x, y); +} + +static int render_middown(const struct gui_rounded_rect *const r, + const short x, const short y) +{ + return render_mid(r, x, y + r->h - refs[GUI_ROUNDED_RECT_MID].h); +} + +static int render(const struct gui_common *const g) +{ + const struct gui_rounded_rect *const r = (const struct gui_rounded_rect *)g; + + short x, y; + + gui_coords(&r->common, &x, &y); + + static int (*const steps[])(const struct gui_rounded_rect *const r, + const short x, const short y) = + { + render_top, + render_rect, + render_midtop, + render_middown, + render_vertleft, + render_vertright, + render_down + }; + + for (size_t i = 0; i < sizeof steps / sizeof *steps; i++) + if (steps[i](r, x, y)) + return -1; + + return 0; +} + +void gui_rounded_rect_init(struct gui_rounded_rect *const r) +{ + *r = (const struct gui_rounded_rect) + { + .common = + { + .render = render + } + }; +} 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; +} |
