aboutsummaryrefslogtreecommitdiff
path: root/src/player
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/player
parentc9e6ae44a9aeb89b3f48f3443d6baa80103f7445 (diff)
downloadjancity-6b9f686913efc3725b2690033cd4f398e07076ba.tar.gz
Add project source code
Diffstat (limited to 'src/player')
-rw-r--r--src/player/CMakeLists.txt16
-rw-r--r--src/player/inc/human_player.h69
-rw-r--r--src/player/inc/player.h71
-rw-r--r--src/player/src/human_player.c570
-rw-r--r--src/player/src/player.c112
5 files changed, 838 insertions, 0 deletions
diff --git a/src/player/CMakeLists.txt b/src/player/CMakeLists.txt
new file mode 100644
index 0000000..d7a275d
--- /dev/null
+++ b/src/player/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_library(player "src/player.c" "src/human_player.c")
+target_include_directories(player PUBLIC "inc")
+target_link_libraries(player
+ PUBLIC
+ building
+ camera
+ gfx
+ instance
+ pad
+ resource
+ tech
+ unit
+ util
+ PRIVATE
+ pad
+ gui)
diff --git a/src/player/inc/human_player.h b/src/player/inc/human_player.h
new file mode 100644
index 0000000..6d97cf7
--- /dev/null
+++ b/src/player/inc/human_player.h
@@ -0,0 +1,69 @@
+#ifndef HUMAN_PLAYER_H
+#define HUMAN_PLAYER_H
+
+#include <camera.h>
+#include <instance.h>
+#include <pad.h>
+#include <player.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum {MAX_SELECTED_INSTANCES = 4};
+
+struct human_player
+{
+ struct player pl;
+ struct camera cam;
+ struct pad pad;
+
+ struct sel_instance
+ {
+ enum sel_type
+ {
+ INSTANCE_TYPE_UNIT,
+ INSTANCE_TYPE_BUILDING,
+ INSTANCE_TYPE_RESOURCE
+ } type;
+
+ union sel_data
+ {
+ const struct unit *u;
+ const struct building *b;
+ const struct resource *r;
+ const struct instance *i;
+ struct unit *rw_u;
+ } d;
+ } sel[MAX_SELECTED_INSTANCES];
+
+ struct
+ {
+ const struct instance *ins;
+ unsigned char t, n;
+ bool render;
+ } target;
+
+ size_t n_sel;
+ bool top_gui;
+ unsigned long gui_res[MAX_RESOURCE_TYPES];
+};
+
+struct human_player_cfg
+{
+ struct player_cfg pl;
+ int padn;
+};
+
+int human_player_init(const struct human_player_cfg *cfg, struct human_player *h);
+bool human_player_update(struct human_player *h, struct player_others *o);
+int human_player_render(const struct human_player *h, const struct player_others *o);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HUMAN_PLAYER_H */
diff --git a/src/player/inc/player.h b/src/player/inc/player.h
new file mode 100644
index 0000000..25f4d81
--- /dev/null
+++ b/src/player/inc/player.h
@@ -0,0 +1,71 @@
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include <building.h>
+#include <camera.h>
+#include <instance.h>
+#include <pad.h>
+#include <resource.h>
+#include <tech.h>
+#include <unit.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef unsigned int player_team;
+
+enum
+{
+ PLAYER_MAX_UNITS = 5,
+ PLAYER_MAX_BUILDINGS = 5
+};
+
+struct player
+{
+ enum player_color
+ {
+ PLAYER_COLOR_BLUE,
+ PLAYER_COLOR_RED
+ } color;
+
+ player_team team;
+ bool alive;
+ struct unit units[PLAYER_MAX_UNITS];
+ struct building buildings[PLAYER_MAX_BUILDINGS];
+ unsigned long resources[MAX_RESOURCE_TYPES];
+ size_t pop, bpop;
+
+ struct
+ {
+ struct unit_tech u;
+ } tree;
+};
+
+struct player_cfg
+{
+ enum player_color color;
+ player_team team;
+ unsigned long x, y;
+};
+
+struct player_others
+{
+ struct player *pl;
+ struct resource *res;
+ size_t n_pl, n_res;
+};
+
+int player_init(const struct player_cfg *cfg, struct player *pl);
+int player_create_unit(const struct unit_cfg *cfg, struct player *pl);
+int player_create_building(const struct building_cfg *cfg, struct player *pl);
+void player_update(struct player *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PLAYER_H */
diff --git a/src/player/src/human_player.c b/src/player/src/human_player.c
new file mode 100644
index 0000000..7fd558a
--- /dev/null
+++ b/src/player/src/human_player.c
@@ -0,0 +1,570 @@
+#include <human_player.h>
+#include <player.h>
+#include <building.h>
+#include <camera.h>
+#include <gui.h>
+#include <instance.h>
+#include <pad.h>
+#include <resource.h>
+#include <unit.h>
+#include <util.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+static bool instance_selected(const struct human_player *const h,
+ const union sel_data *const sel)
+{
+ if (h->n_sel)
+ for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
+ {
+ const struct sel_instance *const si = &h->sel[i];
+
+ if (si && sel->u == si->d.u)
+ return true;
+ }
+
+ return false;
+}
+
+static bool select_units(struct human_player *const h, const short x,
+ const short y)
+{
+ struct player *const pl = &h->pl;
+
+ for (size_t i = 0; i < sizeof pl->units / sizeof *pl->units; i++)
+ {
+ const struct unit *const u = &pl->units[i];
+ const struct instance *const in = &u->instance;
+
+ if (in->alive)
+ {
+ switch (u->state)
+ {
+ default:
+ {
+ const union sel_data d = {.u = u};
+
+ if (!instance_selected(h, &d)
+ && cursor_collision(&h->cam, &in->r)
+ && h->n_sel < sizeof h->sel / sizeof *h->sel)
+ {
+ for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
+ {
+ struct sel_instance *const sel = &h->sel[i];
+
+ if (!sel->d.u)
+ {
+ sel->type = INSTANCE_TYPE_UNIT;
+ sel->d.u = u;
+ h->n_sel++;
+ sfx_play(&unit_sounds[UNIT_SOUND_SELECTED]);
+ return true;
+ }
+ }
+ }
+ }
+ break;
+
+ case UNIT_STATE_SHELTERED:
+ /* Fall through. */
+ case UNIT_STATE_HARVESTING_GOLD:
+ break;
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool select_buildings(struct human_player *const h, const short x,
+ const short y)
+{
+ struct player *const pl = &h->pl;
+
+ for (size_t i = 0; i < sizeof pl->buildings / sizeof *pl->buildings; i++)
+ {
+ const struct building *const b = &pl->buildings[i];
+ const struct instance *const in = &b->instance;
+
+ if (in->alive)
+ {
+ const union sel_data d = {.b = b};
+
+ if (!instance_selected(h, &d)
+ && cursor_collision(&h->cam, &in->r)
+ && h->n_sel < sizeof h->sel / sizeof *h->sel)
+ {
+ for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
+ {
+ struct sel_instance *const sel = &h->sel[i];
+
+ if (!sel->d.b)
+ {
+ sel->type = INSTANCE_TYPE_BUILDING;
+ sel->d.b = b;
+ h->n_sel++;
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool select_resources(struct human_player *const h, const short x,
+ const short y, const struct player_others *const o)
+{
+ for (size_t i = 0; i < o->n_res; i++)
+ {
+ const struct resource *const r = &o->res[i];
+ const struct instance *const in = &r->instance;
+
+ if (in->alive)
+ {
+ const union sel_data d = {.r = r};
+
+ if (!instance_selected(h, &d)
+ && cursor_collision(&h->cam, &in->r)
+ && h->n_sel < sizeof h->sel / sizeof *h->sel)
+ {
+ for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
+ {
+ struct sel_instance *const sel = &h->sel[i];
+
+ if (!sel->d.r)
+ {
+ sel->type = INSTANCE_TYPE_RESOURCE;
+ sel->d.r = r;
+ h->n_sel++;
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static void select_instances(struct human_player *const h,
+ const struct player_others *const o)
+{
+ unsigned long x, y;
+
+ cursor_pos(&h->cam, &x, &y);
+
+ if (!select_buildings(h, x, y)
+ && !select_units(h, x, y))
+ select_resources(h, x, y, o);
+}
+
+static bool instance_collision(const struct camera *const cam,
+ const struct instance *const in)
+{
+ return in->alive && cursor_collision(cam, &in->r);
+}
+
+static void resources_stored(struct instance *const i, void *const op)
+{
+ struct player *const p = op;
+ struct unit *const u = (struct unit *)i;
+ struct unit_harvester *const uh = &u->us.harvester;
+
+ p->resources[uh->type] += uh->carry;
+ uh->carry = 0;
+ unit_set_target(u, &uh->prev_target);
+}
+
+static void harvest_done(struct instance *const ins, void *const op)
+{
+ struct player *const p = op;
+ struct unit *const u = (struct unit *)ins;
+ struct resource *const res = (struct resource *)u->target.ins;
+
+ if (res->type == RESOURCE_TYPE_GOLD)
+ {
+ struct resource_gold *const g = &res->res.gold;
+
+ for (size_t i = 0; i < sizeof g->miners / sizeof *g->miners; i++)
+ {
+ struct instance **const pi = &g->miners[i];
+
+ if (*pi == ins)
+ {
+ *pi = NULL;
+ g->n_miners--;
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < sizeof p->buildings / sizeof *p->buildings; i++)
+ {
+ struct building *const b = &p->buildings[i];
+
+ if (b->instance.alive && b->type == BUILDING_TYPE_BARRACKS)
+ {
+ const struct unit_target t =
+ {
+ .ins = &b->instance,
+ .state = UNIT_STATE_CARRYING,
+ .done = resources_stored,
+ .op = p
+ /* TODO .state = UNIT_STATE_SHELTERED */
+ };
+
+ u->us.harvester.prev_target = u->target;
+ unit_set_target(u, &t);
+ break;
+ }
+ }
+}
+
+static bool target_from_own(struct player *const p,
+ const struct camera *const cam, struct unit_target *const t)
+{
+ for (size_t i = 0; i < sizeof p->buildings / sizeof *p->buildings; i++)
+ {
+ struct building *const b = &p->buildings[i];
+ struct instance *const in = &b->instance;
+
+ if (instance_collision(cam, in))
+ {
+ *t = (const struct unit_target)
+ {
+ .ins = in,
+ /* TODO .state = UNIT_STATE_SHELTERED */
+ };
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool target_from_others(const struct player *const p,
+ const struct player_others *const o,
+ const struct camera *const cam, struct unit_target *const t)
+{
+ for (size_t i = 0; i < o->n_pl; i++)
+ {
+ struct player *const pl = &o->pl[i];
+
+ if (p->team != pl->team)
+ {
+ for (size_t i = 0; i < sizeof pl->units / sizeof *pl->units; i++)
+ {
+ struct unit *const u = &pl->units[i];
+ struct instance *const in = &u->instance;
+
+ if (instance_collision(cam, in))
+ {
+ *t = (const struct unit_target)
+ {
+ .ins = in,
+ .attack = unit_attacked,
+ .state = UNIT_STATE_ATTACKING
+ };
+
+ return true;
+ }
+ }
+
+ for (size_t i = 0; i < sizeof pl->buildings / sizeof *pl->buildings; i++)
+ {
+ struct building *const b = &pl->buildings[i];
+ struct instance *const in = &b->instance;
+
+ if (instance_collision(cam, in))
+ {
+ *t = (const struct unit_target)
+ {
+ .ins = in,
+ .state = UNIT_STATE_ATTACKING
+ };
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static enum unit_state target_state_from_res(const struct resource *const res)
+{
+ switch (res->type)
+ {
+ case RESOURCE_TYPE_GOLD:
+ return UNIT_STATE_HARVESTING_GOLD;
+
+ case RESOURCE_TYPE_WOOD:
+ return UNIT_STATE_HARVESTING_WOOD;
+
+ default:
+ break;
+ }
+
+ return UNIT_STATE_IDLE_MOVING;
+}
+
+static bool target_from_res(struct player *const p,
+ const struct player_others *const o,
+ const struct camera *const cam, struct unit_target *const t)
+{
+ for (size_t i = 0; i < o->n_res; i++)
+ {
+ struct resource *const res = &o->res[i];
+ struct instance *const in = &res->instance;
+
+ if (instance_collision(cam, in))
+ {
+ *t = (const struct unit_target)
+ {
+ .ins = in,
+ .attack = resource_harvested,
+ .shelter = resource_shelter(res),
+ .state = target_state_from_res(res),
+ .done = harvest_done,
+ .op = p
+ };
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void set_target(struct human_player *const h, struct unit *const u,
+ const struct unit_target *const t)
+{
+ unit_set_target(u, t);
+ memset(&h->target, 0, sizeof h->target);
+ h->target.ins = t->ins;
+ h->target.render = true;
+}
+
+static void target_from_pos(struct player *const p,
+ const struct player_others *const o,
+ const struct camera *const cam, struct unit_target *const t)
+{
+ if (!target_from_own(p, cam, t)
+ && !target_from_others(p, o, cam, t)
+ && !target_from_res(p, o, cam, t))
+ t->ins = NULL;
+}
+
+static void move_units(struct human_player *const h,
+ struct player_others *const o)
+{
+ if (!h->n_sel)
+ return;
+
+ unsigned long x, y;
+ struct player *const p = &h->pl;
+ struct unit_target t;
+
+ target_from_pos(p, o, &h->cam, &t);
+ cursor_pos(&h->cam, &x, &y);
+
+ bool unit_move = false;
+
+ for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
+ {
+ struct sel_instance *const si = &h->sel[i];
+
+ if (si->d.u && si->type == INSTANCE_TYPE_UNIT)
+ {
+ struct unit *const u = si->d.rw_u;
+
+ if (!t.ins || !unit_target_valid(u, &t))
+ unit_move_to(u, x, y);
+ else
+ set_target(h, u, &t);
+
+ unit_move = true;
+ }
+ }
+
+ if (unit_move)
+ {
+ const enum unit_sound s = rand() & 1 ?
+ UNIT_SOUND_MOVE : UNIT_SOUND_MOVE_2;
+ sfx_play(&unit_sounds[s]);
+ }
+}
+
+static void deselect_instances(struct human_player *const h)
+{
+ memset(h->sel, 0, sizeof h->sel);
+ h->n_sel = 0;
+}
+
+static void update_selected(struct human_player *const h)
+{
+ if (h->n_sel)
+ {
+ for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
+ {
+ struct sel_instance *const si = &h->sel[i];
+
+ if (si->d.i && (!si->d.i->alive || si->d.i->dying
+ || (si->type == INSTANCE_TYPE_UNIT
+ && (si->d.u->state == UNIT_STATE_HARVESTING_GOLD
+ || si->d.u->state == UNIT_STATE_SHELTERED))))
+ {
+ si->d.i = NULL;
+ h->n_sel--;
+ }
+ }
+ }
+}
+
+static void update_target(struct human_player *const h)
+{
+ const struct instance *const i = h->target.ins;
+
+ if (i)
+ {
+ enum {TOGGLE = 10};
+
+ if (!i->alive)
+ memset(&h->target, 0, sizeof h->target);
+ else if (++h->target.t >= TOGGLE)
+ {
+ enum {TIMES = 5};
+
+ h->target.render ^= true;
+ h->target.t = 0;
+
+ if (++h->target.n >= TIMES)
+ memset(&h->target, 0, sizeof h->target);
+ }
+ }
+}
+
+bool human_player_update(struct human_player *const h,
+ struct player_others *const o)
+{
+ bool ret = false;
+ struct player *const p = &h->pl;
+
+ if (p->alive)
+ {
+ gui_update(h);
+ pad_update(&h->pad);
+ update_selected(h);
+ update_target(h);
+
+ if (pad_justpressed(&h->pad, PAD_KEY_OPTIONS))
+ ret = true;
+ else if (pad_justpressed(&h->pad, PAD_KEY_A))
+ select_instances(h, o);
+ else if (pad_justpressed(&h->pad, PAD_KEY_B))
+ move_units(h, o);
+ else if (pad_justpressed(&h->pad, PAD_KEY_C))
+ deselect_instances(h);
+ else if (pad_justpressed(&h->pad, PAD_KEY_E))
+ h->top_gui ^= true;
+
+ camera_update(&h->cam, &h->pad);
+ player_update(p);
+ }
+
+ return ret;
+}
+
+static int render_target(const struct human_player *const h)
+{
+ const struct instance *const i = h->target.ins;
+
+ if (!i || !i->alive || !h->target.render)
+ return 0;
+
+ return instance_render_target(i, &h->cam);
+}
+
+static int render_own_units(const struct human_player *const h)
+{
+ const struct player *const p = &h->pl;
+
+ for (size_t i = 0; i < sizeof p->units / sizeof *p->units; i++)
+ {
+ const struct unit *const u = &p->units[i];
+ const union sel_data d = {.u = u};
+ const bool sel = instance_selected(h, &d);
+
+ if (unit_render(u, &h->cam, sel))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int render_own_buildings(const struct human_player *const h)
+{
+ const struct player *const p = &h->pl;
+
+ for (size_t i = 0; i < sizeof p->buildings / sizeof *p->buildings; i++)
+ {
+ const struct building *const b = &p->buildings[i];
+ const union sel_data d = {.b = b};
+ const bool sel = instance_selected(h, &d);
+
+ if (building_render(b, &h->cam, sel))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int render_resources(const struct human_player *const h,
+ const struct resource *const res, const size_t n)
+{
+ for (size_t i = 0; i < n; i++)
+ {
+ const struct resource *const r = &res[i];
+ const union sel_data d = {.r = r};
+ const bool sel = instance_selected(h, &d);
+
+ if (resource_render(r, &h->cam, sel))
+ return -1;
+ }
+
+ return 0;
+}
+
+int human_player_render(const struct human_player *const h,
+ const struct player_others *const o)
+{
+ if (render_target(h)
+ || render_own_units(h)
+ || render_own_buildings(h)
+ || render_resources(h, o->res, o->n_res)
+ || gui_render(h))
+ return -1;
+
+ return 0;
+}
+
+int human_player_init(const struct human_player_cfg *const cfg,
+ struct human_player *const h)
+{
+ memset(h, 0, sizeof *h);
+
+ if (player_init(&cfg->pl, &h->pl))
+ return -1;
+
+ pad_init(cfg->padn, &h->pad);
+ cursor_init(&h->cam.cursor);
+ h->top_gui = true;
+ memmove(h->gui_res, h->pl.resources, sizeof h->gui_res);
+ return 0;
+}
diff --git a/src/player/src/player.c b/src/player/src/player.c
new file mode 100644
index 0000000..a638cb8
--- /dev/null
+++ b/src/player/src/player.c
@@ -0,0 +1,112 @@
+#include <player.h>
+#include <building.h>
+#include <unit.h>
+#include <stddef.h>
+
+void player_update(struct player *const p)
+{
+ for (size_t i = 0; i < sizeof p->units / sizeof *p->units; i++)
+ {
+ struct unit *const u = &p->units[i];
+
+ unit_update(&p->tree.u, u);
+ }
+}
+
+int player_create_unit(const struct unit_cfg *const cfg, struct player *const pl)
+{
+ enum {N = sizeof pl->units / sizeof *pl->units};
+
+ if (pl->pop < N)
+ {
+ for (size_t i = 0; i < N; i++)
+ {
+ struct unit *const u = &pl->units[i];
+ struct instance *const i = &u->instance;
+
+ if (!i->alive)
+ {
+ unit_create(cfg, u);
+ pl->pop++;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int player_create_building(const struct building_cfg *const cfg, struct player *const pl)
+{
+ enum {N = sizeof pl-> buildings / sizeof *pl->buildings};
+
+ if (pl->bpop < N)
+ {
+ for (size_t i = 0; i < N; i++)
+ {
+ struct building *const b = &pl->buildings[i];
+ struct instance *const i = &b->instance;
+
+ if (!i->alive)
+ {
+ building_create(cfg, b);
+ pl->bpop++;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int player_init(const struct player_cfg *const cfg, struct player *const pl)
+{
+ const unsigned long x = cfg->x, y = cfg->y;
+
+ const struct building_cfg bcfg =
+ {
+ .type = BUILDING_TYPE_BARRACKS,
+ .x = x,
+ .y = y
+ };
+
+ const struct unit_cfg cfgs[] =
+ {
+ {
+ .type = UNIT_TYPE_PEASANT,
+ .x = x + 80,
+ .y = y
+ },
+
+ {
+ .type = UNIT_TYPE_PEASANT,
+ .x = x + 80,
+ .y = y + 40
+ },
+
+ {
+ .type = UNIT_TYPE_PEASANT,
+ .x = x + 100,
+ .y = y + 20
+ }
+ };
+
+ if (player_create_building(&bcfg, pl))
+ return -1;
+
+ for (size_t i = 0; i < sizeof cfgs / sizeof *cfgs; i++)
+ if (player_create_unit(&cfgs[i], pl))
+ return -1;
+
+ for (size_t i = 0; i < sizeof pl->resources / sizeof *pl->resources; i++)
+ {
+ enum {DEFAULT_RES = 120};
+
+ pl->resources[i] = DEFAULT_RES;
+ }
+
+ pl->alive = true;
+ pl->color = cfg->color;
+ pl->team = cfg->team;
+ return 0;
+}