diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2021-07-03 00:49:03 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-03-30 08:20:20 +0200 |
| commit | 6b9f686913efc3725b2690033cd4f398e07076ba (patch) | |
| tree | e9aa91a6b9f617d78123ebe7ad272fc42a60d306 /src/player | |
| parent | c9e6ae44a9aeb89b3f48f3443d6baa80103f7445 (diff) | |
| download | jancity-6b9f686913efc3725b2690033cd4f398e07076ba.tar.gz | |
Add project source code
Diffstat (limited to 'src/player')
| -rw-r--r-- | src/player/CMakeLists.txt | 16 | ||||
| -rw-r--r-- | src/player/inc/human_player.h | 69 | ||||
| -rw-r--r-- | src/player/inc/player.h | 71 | ||||
| -rw-r--r-- | src/player/src/human_player.c | 570 | ||||
| -rw-r--r-- | src/player/src/player.c | 112 |
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; +} |
