aboutsummaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2021-07-03 00:49:03 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2022-03-30 08:20:20 +0200
commit6b9f686913efc3725b2690033cd4f398e07076ba (patch)
treee9aa91a6b9f617d78123ebe7ad272fc42a60d306 /src/gui
parentc9e6ae44a9aeb89b3f48f3443d6baa80103f7445 (diff)
downloadjancity-6b9f686913efc3725b2690033cd4f398e07076ba.tar.gz
Add project source code
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/CMakeLists.txt3
-rw-r--r--src/gui/inc/gui.h36
-rw-r--r--src/gui/src/gui.c473
3 files changed, 512 insertions, 0 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
new file mode 100644
index 0000000..59ee94d
--- /dev/null
+++ b/src/gui/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_library(gui "src/gui.c")
+target_include_directories(gui PUBLIC "inc")
+target_link_libraries(gui PUBLIC gfx player PRIVATE font unit building)
diff --git a/src/gui/inc/gui.h b/src/gui/inc/gui.h
new file mode 100644
index 0000000..254fd47
--- /dev/null
+++ b/src/gui/inc/gui.h
@@ -0,0 +1,36 @@
+#ifndef GUI_H
+#define GUI_H
+
+#include <gfx.h>
+#include <human_player.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum
+{
+ 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
+};
+
+void gui_update(struct human_player *h);
+int gui_render(const struct human_player *h);
+
+extern struct sprite gui_sprites[MAX_GUI_SPRITES];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GUI_H */
diff --git a/src/gui/src/gui.c b/src/gui/src/gui.c
new file mode 100644
index 0000000..2a5e53b
--- /dev/null
+++ b/src/gui/src/gui.c
@@ -0,0 +1,473 @@
+#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)
+{
+ struct sprite *const s = sprite_get();
+
+ if (!s
+ || sprite_clone(&gui_sprites[GUI_BAR_LEFT], s))
+ return -1;
+
+ sprite_sort(s);
+ return 0;
+}
+
+static int render_topright(void)
+{
+ struct sprite *const s = sprite_get();
+
+ if (!s || 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)
+ {
+ struct sprite *const m = sprite_get();
+
+ if (!m
+ || 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);
+
+ struct sprite *const right = sprite_get();
+
+ if (!right
+ || 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)
+{
+ 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;
+
+ 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)
+ {
+ struct sprite *const v = sprite_get();
+
+ if (!v)
+ return -1;
+
+ *v = gui_sprites[GUI_SELECTION_MID_VERT];
+ 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_selvertleft(const struct sprite *const up)
+{
+ return render_selvert(up, up->x);
+}
+
+static int render_selvertright(const struct sprite *const up)
+{
+ return render_selvert(up, screen_w - gui_sprites[GUI_SELECTION_MID_VERT].w - up->x);
+}
+
+static int render_seldown(const struct sprite *const up)
+{
+ {
+ struct sprite *const left = sprite_get();
+
+ if (!left)
+ return -1;
+
+ *left = gui_sprites[GUI_SELECTION_DOWN_LEFT];
+ left->x = up->x;
+ left->y = screen_h - left->h;
+ sprite_sort(left);
+ }
+
+ {
+ struct sprite *const right = sprite_get();
+
+ if (!right)
+ return -1;
+
+ *right = gui_sprites[GUI_SELECTION_DOWN_RIGHT];
+ 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)
+{
+ struct rect *const sel = rect_get(true);
+
+ if (!sel)
+ return -1;
+
+ 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
+ {
+ size_t i;
+ short x;
+ } a = {.i = 0, .x = up->x + up->w};
+ a.i < n_mid;
+ a.i++, a.x += mid_w)
+ {
+ struct sprite *const m = sprite_get();
+
+ if (!m)
+ return -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;
+}
+
+static int render_selmidtop(const struct sprite *const up)
+{
+ return render_selmid(up, up->y);
+}
+
+static int render_selmiddown(const struct sprite *const up)
+{
+ return render_selmid(up, screen_h - gui_sprites[GUI_SELECTION_MID].h);
+}
+
+static int render_topmenu(const struct human_player *const h)
+{
+ 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))
+ 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;
+
+ {
+ struct rect *const gr = rect_get(true);
+
+ if (!gr)
+ return -1;
+
+ gr->x = x;
+ gr->y = ry;
+ gr->w = gw;
+ gr->h = HEIGHT;
+ gr->g = UCHAR_MAX >> 1;
+ rect_sort(gr);
+ }
+
+ {
+ struct rect *const rr = rect_get(true);
+
+ if (!rr)
+ return -1;
+
+ 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};
+ struct rect *const r = rect_get(true);
+
+ if (!r)
+ return -1;
+
+ 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 (uh->carry)
+ return font_printf(f, x, y, "%d", uh->carry) < 0;
+ }
+
+ 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)
+{
+ 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)
+ return -1;
+
+ return 0;
+}
+
+static int render_sel(const struct human_player *const h)
+{
+ struct sprite *const up = sprite_get();
+
+ if (!up
+ || 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))
+ 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;
+
+ return 0;
+}