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/unit | |
| parent | c9e6ae44a9aeb89b3f48f3443d6baa80103f7445 (diff) | |
| download | jancity-6b9f686913efc3725b2690033cd4f398e07076ba.tar.gz | |
Add project source code
Diffstat (limited to 'src/unit')
| -rw-r--r-- | src/unit/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/unit/inc/unit.h | 134 | ||||
| -rw-r--r-- | src/unit/inc/unit_type.h | 11 | ||||
| -rw-r--r-- | src/unit/src/unit.c | 660 |
4 files changed, 808 insertions, 0 deletions
diff --git a/src/unit/CMakeLists.txt b/src/unit/CMakeLists.txt new file mode 100644 index 0000000..460cf43 --- /dev/null +++ b/src/unit/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(unit "src/unit.c") +target_include_directories(unit PUBLIC "inc") +target_link_libraries(unit PUBLIC fixmath container camera gfx instance resource sfx tech util) diff --git a/src/unit/inc/unit.h b/src/unit/inc/unit.h new file mode 100644 index 0000000..4a5ace7 --- /dev/null +++ b/src/unit/inc/unit.h @@ -0,0 +1,134 @@ +#ifndef UNIT_H +#define UNIT_H + +#include <camera.h> +#include <gfx.h> +#include <sfx.h> +#include <instance.h> +#include <resource_type.h> +#include <tech.h> +#include <unit_type.h> +#include <util.h> +#include <fixmath.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct unit_tech +{ + enum tech_level carry; +}; + +enum unit_state +{ + UNIT_STATE_IDLE_MOVING, + UNIT_STATE_SHELTERED, + UNIT_STATE_HARVESTING_WOOD, + UNIT_STATE_HARVESTING_GOLD, + UNIT_STATE_CARRYING, + UNIT_STATE_ATTACKING +}; + +struct unit_target +{ + struct instance *ins; + enum unit_state state; + instance_sheltered_cb shelter; + instance_attacked_cb attack; + instance_done_cb done; + void *op; +}; + +struct unit +{ + struct instance instance; + enum unit_type type; + + enum unit_dir + { + UNIT_DIR_N, + UNIT_DIR_NE, + UNIT_DIR_E, + UNIT_DIR_SE, + UNIT_DIR_S, + UNIT_DIR_SW, + UNIT_DIR_W, + UNIT_DIR_NW, + + MAX_UNIT_DIRECTIONS + } dir; + + enum unit_state state; + + struct + { + unsigned char t, i; + } frame; + + union + { + struct unit_harvester + { + enum resource_type type; + unsigned char carry, t; + struct unit_target prev_target; + } harvester; + } us; + + fix16_t rx, ry, tx, ty; + + struct unit_target target; +}; + +UTIL_STATIC_ASSERT(!offsetof(struct unit, instance), "must be at offset zero"); + +struct unit_cfg +{ + enum unit_type type; + unsigned long x, y; +}; + +void unit_create(const struct unit_cfg *cfg, struct unit *u); +int unit_render(const struct unit *u, const struct camera *cam, bool sel); +bool unit_can_harvest(const struct unit *u); +bool unit_target_valid(const struct unit *u, const struct unit_target *t); +void unit_set_target(struct unit *u, const struct unit_target *t); +void unit_move_to(struct unit *u, unsigned long x, unsigned long y); +bool unit_attacked(struct instance *, instance_hp ap); +void unit_update(const struct unit_tech *t, struct unit *u); +instance_hp unit_maxhp(const struct unit *u); +const char *unit_str(const struct unit *u); + +enum +{ + UNIT_SPRITE_N, + UNIT_SPRITE_NE, + UNIT_SPRITE_E, + UNIT_SPRITE_SE, + UNIT_SPRITE_S, + + MAX_UNIT_SPRIES +}; + +extern struct sprite unit_sprites[MAX_UNIT_SPRIES]; + +enum unit_sound +{ + UNIT_SOUND_SELECTED, + UNIT_SOUND_MOVE, + UNIT_SOUND_MOVE_2, + + MAX_UNIT_SOUNDS +}; + +extern struct sound unit_sounds[MAX_UNIT_SOUNDS]; + +#ifdef __cplusplus +} +#endif + +#endif /* UNIT_H */ diff --git a/src/unit/inc/unit_type.h b/src/unit/inc/unit_type.h new file mode 100644 index 0000000..fddd5c9 --- /dev/null +++ b/src/unit/inc/unit_type.h @@ -0,0 +1,11 @@ +#ifndef UNIT_TYPE_H +#define UNIT_TYPE_H + +enum unit_type +{ + UNIT_TYPE_PEASANT, + + MAX_UNIT_TYPES +}; + +#endif /* UNIT_TYPE_H */ diff --git a/src/unit/src/unit.c b/src/unit/src/unit.c new file mode 100644 index 0000000..bb8e243 --- /dev/null +++ b/src/unit/src/unit.c @@ -0,0 +1,660 @@ +#include <unit.h> +#include <camera.h> +#include <gfx.h> +#include <sfx.h> +#include <fixmath.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +struct sprite unit_sprites[MAX_UNIT_SPRIES]; +struct sound unit_sounds[MAX_UNIT_SOUNDS]; + +enum {N_FRAMES = 5}; + +static void move_unit(struct unit *const u, const fix16_t sx, const fix16_t sy) +{ + switch (u->dir) + { + case UNIT_DIR_N: + u->ry -= sy; + break; + + case UNIT_DIR_NE: + u->rx += sx; + u->ry -= sy; + break; + + case UNIT_DIR_E: + u->rx += sx; + break; + + case UNIT_DIR_SE: + u->rx += sx; + u->ry += sy; + break; + + case UNIT_DIR_S: + u->ry += sy; + break; + + case UNIT_DIR_SW: + u->rx -= sx; + u->ry += sy; + break; + + case UNIT_DIR_W: + u->rx -= sx; + break; + + case UNIT_DIR_NW: + u->rx -= sx; + u->ry -= sy; + break; + + default: + break; + } +} + +static void get_speed(const struct unit *const u, fix16_t *const x, + fix16_t *const y) +{ + static const struct speed + { + fix16_t x, y; + } speed[] = + { + [UNIT_TYPE_PEASANT] = + { + .x = FIX16_C_FROM_INT(1), + .y = FIX16_C_FROM_INT(1) + } + }; + + const struct speed *const s = &speed[u->type]; + const int dx = abs(u->rx - u->tx); + const int dy = abs(u->ry - u->ty); + + *x = dx < s->x ? dx : s->x; + *y = dy < s->y ? dy : s->y; +} + +static enum unit_dir get_direction(const struct unit *const u) +{ + const fix16_t x = u->rx, y = u->ry, tx = u->tx, ty = u->ty; + enum unit_dir dir = 0; + + if (x != tx && y != ty) + { + if (x > tx && y > ty) + dir = UNIT_DIR_NW; + else if (x < tx && y > ty) + dir = UNIT_DIR_NE; + else if (x < tx && y < ty) + dir = UNIT_DIR_SE; + else + dir = UNIT_DIR_SW; + } + else if (x != tx) + { + if (x > tx) + dir = UNIT_DIR_W; + else + dir = UNIT_DIR_E; + } + else if (y != ty) + { + if (y > ty) + dir = UNIT_DIR_N; + else + dir = UNIT_DIR_S; + } + + return dir; +} + +static instance_hp attack_points(const struct unit *const u) +{ + static instance_hp ap[] = + { + [UNIT_TYPE_PEASANT] = 1 + }; + + return ap[u->type]; +} + +static void unit_stop(struct unit *const u) +{ + u->tx = u->rx; + u->ty = u->ry; +} + +static void target_reset(struct unit *const u) +{ + memset(&u->target, 0, sizeof u->target); + u->state = UNIT_STATE_IDLE_MOVING; + unit_stop(u); +} + +static void unit_chase(const struct instance *const i, struct unit *const u) +{ + u->tx = fix16_from_int(i->r.x + (i->r.w >> 1)); + u->ty = fix16_from_int(i->r.y + (i->r.h >> 1)); + u->state = UNIT_STATE_IDLE_MOVING; +} + +static void target_interact(struct unit *const u) +{ + struct unit_target *const t = &u->target; + const struct instance *const ins = t->ins; + + /* TODO: lose u->ti if not visible. */ + if (ins->alive) + { + if (t->state != u->state) + { + struct instance *const ui = &u->instance; + + if (util_collision(&ins->r, &ui->r)) + { + switch (t->state) + { + case UNIT_STATE_CARRYING: + u->target.done(&u->instance, u->target.op); + break; + + case UNIT_STATE_ATTACKING: + t->attack(t->ins, attack_points(u)); + u->state = t->state; + break; + + case UNIT_STATE_HARVESTING_WOOD: + { + struct unit_harvester *const uh = &u->us.harvester; + + if (uh->type != RESOURCE_TYPE_WOOD) + uh->carry = 0; + + uh->type = RESOURCE_TYPE_WOOD; + u->state = t->state; + unit_stop(u); + } + break; + + case UNIT_STATE_HARVESTING_GOLD: + { + struct unit_harvester *const uh = &u->us.harvester; + + if (uh->type != RESOURCE_TYPE_GOLD) + uh->carry = 0; + + uh->type = RESOURCE_TYPE_GOLD; + + if (t->shelter(t->ins, ui)) + u->state = t->state; + else + unit_stop(u); + } + break; + + case UNIT_STATE_SHELTERED: + u->state = t->state; + break; + + default: + break; + } + } + else + unit_chase(ins, u); + } + } + else + target_reset(u); +} + +static bool must_move(const struct unit *const u) +{ + return u->rx != u->tx || u->ry != u->ty; +} + +static void update_harvest(const struct unit_tech *const t, struct unit *const u) +{ + static const unsigned char carry[] = + { + [TECH_LEVEL_1] = 10, + [TECH_LEVEL_2] = 15, + [TECH_LEVEL_3] = 25 + }; + + static const char inc[] = + { + [TECH_LEVEL_1] = 10, + [TECH_LEVEL_2] = 50, + [TECH_LEVEL_3] = 25 + }; + + struct unit_harvester *const uh = &u->us.harvester; + bool ret = false; + + if (++uh->t >= inc[t->carry]) + { + if (uh->carry < carry[t->carry]) + { + ++uh->carry; + ret = u->target.attack(u->target.ins, 1); + } + + uh->t = 0; + } + + if (ret || uh->carry >= carry[t->carry]) + { + u->state = UNIT_STATE_CARRYING; + u->target.done(&u->instance, u->target.op); + } +} + +void unit_update(const struct unit_tech *const t, struct unit *const u) +{ + const struct instance *const i = &u->instance; + + if (i->alive) + { + if (u->target.ins) + target_interact(u); + + switch (u->state) + { + case UNIT_STATE_SHELTERED: + break; + + case UNIT_STATE_HARVESTING_GOLD: + /* Fall through. */ + case UNIT_STATE_HARVESTING_WOOD: + update_harvest(t, u); + break; + + case UNIT_STATE_ATTACKING: + if (must_move(u)) + u->state = UNIT_STATE_IDLE_MOVING; + + break; + + case UNIT_STATE_CARRYING: + /* Fall through. */ + case UNIT_STATE_IDLE_MOVING: + + if (must_move(u)) + { + fix16_t x_step, y_step; + + u->dir = get_direction(u); + get_speed(u, &x_step, &y_step); + move_unit(u, x_step, y_step); + + enum {FRAME_RATE = 6}; + + if (++u->frame.t >= FRAME_RATE) + { + u->frame.t = 0; + + if (++u->frame.i >= N_FRAMES) + u->frame.i = 0; + } + + u->state = UNIT_STATE_IDLE_MOVING; + } + else + u->frame.i = 0; + + u->instance.r.x = fix16_to_int(u->rx); + u->instance.r.y = fix16_to_int(u->ry); + break; + } + } +} + +bool can_attack(const struct unit *const u) +{ + static const bool a[] = + { + [UNIT_TYPE_PEASANT] = true + }; + + return a[u->type]; +} + +static bool can_shelter(const struct unit *const u) +{ + static const bool s[] = + { + [UNIT_TYPE_PEASANT] = true + }; + + return s[u->type]; +} + +bool unit_can_harvest(const struct unit *const u) +{ + static const bool h[] = + { + [UNIT_TYPE_PEASANT] = true + }; + + return h[u->type]; +} + +bool unit_attacked(struct instance *const i, const instance_hp ap) +{ + return instance_attacked(i, ap); +} + +bool unit_target_valid(const struct unit *const u, + const struct unit_target *const t) +{ + switch (t->state) + { + case UNIT_STATE_HARVESTING_GOLD: + /* Fall through. */ + case UNIT_STATE_HARVESTING_WOOD: + return unit_can_harvest(u); + + case UNIT_STATE_ATTACKING: + return can_attack(u); + + case UNIT_STATE_SHELTERED: + if (t->shelter) + return can_shelter(u); + else + break; + + case UNIT_STATE_IDLE_MOVING: + /* Fall through. */ + case UNIT_STATE_CARRYING: + break; + } + + return false; +} + +void unit_set_target(struct unit *const u, const struct unit_target *const t) +{ + const struct instance *const ti = t->ins; + + u->target = *t; + unit_chase(ti, u); +} + +void unit_move_to(struct unit *const u, const unsigned long x, const unsigned long y) +{ + const struct instance *const i = &u->instance; + const unsigned long x_off = i->r.w >> 1; + const unsigned long y_off = i->r.h >> 1; + + u->target.ins = NULL; + u->state = UNIT_STATE_IDLE_MOVING; + u->tx = x > x_off ? fix16_from_int(x - x_off) : 0; + u->ty = y > y_off ? fix16_from_int(y - y_off) : 0; +} + +instance_hp unit_maxhp(const struct unit *const u) +{ + static const instance_hp hp[] = + { + [UNIT_TYPE_PEASANT] = 25 + }; + + return hp[u->type]; +} + +static int get_ux(const struct unit *const u) +{ + switch (u->dir) + { + case UNIT_DIR_N: + return 0; + + case UNIT_DIR_NE: + /* Fall through. */ + case UNIT_DIR_NW: + return 1; + + case UNIT_DIR_E: + /* Fall through. */ + case UNIT_DIR_W: + return 2; + + case UNIT_DIR_SE: + /* Fall through. */ + case UNIT_DIR_SW: + return 3; + + case UNIT_DIR_S: + return 4; + + default: + break; + } + + return -1; +} + +typedef const struct +{ + const struct sprite *s; + char xo, x[N_FRAMES], w[N_FRAMES]; + short y; + short h; +} anim_dim; + +static anim_dim *peasant_anim(const struct unit *const u) +{ + static anim_dim t[] = + { + { + .s = &unit_sprites[UNIT_SPRITE_N], + .xo = 5, + .x = {0, 1, 1, 2, 1}, + .w = {25, 22, 24, 22, 24}, + .y = 2, + .h = 31 + }, + + { + .s = &unit_sprites[UNIT_SPRITE_NE], + .xo = 11, + .x = {0, -4, -1, -4, -2}, + .w = {18, 26, 22, 23, 20}, + .y = 2, + .h = 31 + }, + + { + .s = &unit_sprites[UNIT_SPRITE_E], + .xo = 10, + .x = {0, -6, -1, -6, -3}, + .w = {14, 26, 17, 24, 19}, + .y = 2, + .h = 33 + }, + + { + .s = &unit_sprites[UNIT_SPRITE_SE], + .xo = 6, + .x = {0, 1, 2, 0, 0}, + .w = {20, 22, 18, 21, 21}, + .y = 2, + .h = 31 + }, + + { + .s = &unit_sprites[UNIT_SPRITE_S], + .xo = 7, + .x = {0, 1, 0, 0, 0}, + .w = {24, 23, 24, 23, 24}, + .y = 2, + .h = 33 + } + }; + + const int ux = get_ux(u); + + if (ux < 0) + return NULL; + + return &t[ux]; +} + +struct render_cfg +{ + struct instance_render_off off; + struct instance_render_quad qcfg; +}; + +static anim_dim *peasant_quad(const struct unit *const u) +{ + return peasant_anim(u); +} + +static void adjust_quad(const struct unit *const u, anim_dim *const dim, + struct render_cfg *const rcfg) +{ + const unsigned char n = u->frame.i; + short u_off = 0; + + for (unsigned char i = 0; i < n; i++) + u_off += dim->w[i]; + + struct instance_render_quad *const qcfg = &rcfg->qcfg; + + qcfg->u = u_off; + qcfg->w = dim->w[n]; + qcfg->h = dim->h; + + struct instance_render_off *const off = &rcfg->off; + + off->x = dim->xo; + off->y = dim->y; + + switch (u->dir) + { + case UNIT_DIR_SW: + /* Fall through. */ + case UNIT_DIR_W: + /* Fall through. */ + case UNIT_DIR_NW: + qcfg->xflip = true; + off->x += dim->x[n]; + break; + + default: + qcfg->xflip = false; + off->x += dim->x[n]; + break; + } +} + +static int unit_quad(const struct unit *const u, struct render_cfg *const rcfg) +{ + struct instance_render_quad *const qcfg = &rcfg->qcfg; + + qcfg->q = quad_get(); + + if (!qcfg->q) + return -1; + + static anim_dim *(*const f[])(const struct unit *) = + { + [UNIT_TYPE_PEASANT] = peasant_quad + }; + + anim_dim *const dim = f[u->type](u); + + if (!dim) + return -1; + + adjust_quad(u, dim, rcfg); + + if (quad_from_sprite(dim->s, qcfg->q)) + return -1; + + return 0; +} + +int unit_render(const struct unit *const u, const struct camera *const cam, + const bool sel) +{ + if (!u->instance.alive + || u->state == UNIT_STATE_SHELTERED + || u->state == UNIT_STATE_HARVESTING_GOLD) + return 0; + + struct render_cfg rcfg; + + if (unit_quad(u, &rcfg)) + return -1; + + const struct instance_render_cfg cfg = + { + .i = &u->instance, + .prim_type = INSTANCE_RENDER_CFG_QUAD, + .prim = {.quad = &rcfg.qcfg}, + .cam = cam, + .sel = sel, + .max_hp = unit_maxhp(u), + .off = &rcfg.off + }; + + return instance_render(&cfg); +} + +static void get_dimensions(const enum unit_type type, short *const w, + short *const h) +{ + static const struct dim + { + short w, h; + } dim[] = + { + [UNIT_TYPE_PEASANT] = {.w = 36, .h = 36} + }; + + const struct dim *const d = &dim[type]; + *w = d->w; + *h = d->h; +} + +void unit_create(const struct unit_cfg *const cfg, struct unit *const u) +{ + struct instance *const i = &u->instance; + + *u = (const struct unit) + { + .instance = + { + .alive = true, + .hp = unit_maxhp(u) + }, + + .type = cfg->type, + .dir = UNIT_DIR_S, + .rx = fix16_from_int(cfg->x), + .ry = fix16_from_int(cfg->y) + }; + + get_dimensions(cfg->type, &i->r.w, &i->r.h); + unit_stop(u); +} + +const char *unit_str(const struct unit *const u) +{ + static const char *const str[] = + { + [UNIT_TYPE_PEASANT] = "Peasant" + }; + + return str[u->type]; +} |
