jancity/src/unit/src/unit.c

474 lines
9.5 KiB
C

#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_SPRITES];
struct sound unit_sounds[MAX_UNIT_SOUNDS];
static const unsigned char anim[] = {0, 1, 0, 2};
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_E:
u->rx += sx;
break;
case UNIT_DIR_S:
u->ry += sy;
break;
case UNIT_DIR_W:
u->rx -= sx;
break;
default:
break;
}
}
static void get_speed(const struct unit *const u, fix16_t *const x,
fix16_t *const y)
{
struct speed
{
fix16_t x, y;
} s;
switch (u->type)
{
case UNIT_TYPE_1:
case UNIT_TYPE_2:
case UNIT_TYPE_3:
case UNIT_TYPE_4:
case UNIT_TYPE_5:
case UNIT_TYPE_6:
{
static const struct speed ref =
{
.x = FIX16_C_FROM_FLOAT(0.4),
.y = FIX16_C_FROM_FLOAT(0.4)
};
s = ref;
}
break;
case UNIT_TYPE_CAR_1:
case UNIT_TYPE_CAR_2:
{
static const struct speed ref =
{
.x = FIX16_C_FROM_FLOAT(1.5),
.y = FIX16_C_FROM_FLOAT(1.5)
};
s = ref;
}
case MAX_UNIT_TYPES:
break;
}
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)
{
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 void unit_stop(struct unit *const u)
{
u->tx = u->rx;
u->ty = u->ry;
}
static void target_reset(struct unit *const u)
{
u->target = (const struct unit_target){0};
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)
{
target_reset(u);
return;
}
else if (t->state == u->state)
return;
struct instance *const ui = &u->instance;
if (!util_collision(&ins->r, &ui->r))
unit_chase(ins, u);
}
static bool must_move(const struct unit *const u)
{
return u->rx != u->tx || u->ry != u->ty;
}
void unit_update(struct unit *const u)
{
struct instance *const i = &u->instance;
if (!i->alive)
return;
if (u->target.ins)
target_interact(u);
else 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 = 10};
if (++u->frame.t >= FRAME_RATE)
{
u->frame.t = 0;
if (++u->frame.i >= sizeof anim / sizeof *anim)
u->frame.i = 0;
}
u->state = UNIT_STATE_IDLE_MOVING;
}
else
u->frame.i = 0;
i->r.x = fix16_to_int(u->rx);
i->r.y = fix16_to_int(u->ry);
}
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;
}
static void adjust_walker(const struct unit *const u, struct sprite *const s,
struct instance_render_off *const off)
{
s->w = u->instance.r.w;
s->h = u->instance.r.h;
s->u += u->dir * s->w;
s->v += anim[u->frame.i] * s->h;
off->x = 0;
off->y = -2;
}
static void adjust_car(const struct unit *const u, struct sprite *const s,
struct instance_render_off *const off)
{
switch (u->dir)
{
case UNIT_DIR_W:
s->w = s->h = 32;
s->u += 32;
off->x = -5;
break;
case UNIT_DIR_S:
s->w = 16;
s->h = 32;
off->x = 2;
break;
case UNIT_DIR_N:
s->u += 16;
s->w = 16;
s->h = 32;
off->x = 2;
break;
case UNIT_DIR_E:
s->w = s->h = 32;
s->u += 64;
off->x = -5;
break;
}
off->y = -10;
}
static void adjust_sprite(const struct unit *const u, struct sprite *const s,
struct instance_render_off *const off)
{
switch (u->type)
{
case UNIT_TYPE_1:
case UNIT_TYPE_2:
case UNIT_TYPE_3:
case UNIT_TYPE_4:
case UNIT_TYPE_5:
case UNIT_TYPE_6:
adjust_walker(u, s, off);
break;
case UNIT_TYPE_CAR_1:
case UNIT_TYPE_CAR_2:
adjust_car(u, s, off);
break;
case MAX_UNIT_TYPES:
break;
}
}
int unit_render(const struct unit *const u, const struct camera *const cam,
const bool sel)
{
if (!u->instance.alive)
return 0;
sprite_get_or_ret(s, -1);
struct instance_render_off off;
if (sprite_clone(&unit_sprites[u->type], s))
{
fprintf(stderr, "%s: sprite_clone failed\n", __func__);
return -1;
}
adjust_sprite(u, s, &off);
const struct instance_render_cfg cfg =
{
.i = &u->instance,
.prim_type = INSTANCE_RENDER_CFG_SPRITE,
.prim.s = s,
.off = &off,
.cam = cam,
.sel = sel
};
return instance_render(&cfg);
}
static void get_dimensions(const enum unit_type type, short *const w,
short *const h)
{
switch (type)
{
case UNIT_TYPE_1:
case UNIT_TYPE_2:
case UNIT_TYPE_3:
case UNIT_TYPE_4:
case UNIT_TYPE_5:
case UNIT_TYPE_6:
*w = *h = 16;
break;
case UNIT_TYPE_CAR_1:
case UNIT_TYPE_CAR_2:
*w = *h = 22;
break;
case MAX_UNIT_TYPES:
break;
}
}
static const char *get_walker_name(const enum unit_type type)
{
enum {FEMALE, MALE} gender = 0;
switch (type)
{
case UNIT_TYPE_2:
case UNIT_TYPE_5:
gender = FEMALE;
break;
case UNIT_TYPE_1:
case UNIT_TYPE_3:
case UNIT_TYPE_4:
case UNIT_TYPE_6:
gender = MALE;
break;
default:
return NULL;
}
static const char *const female[] =
{
"Amelia",
"Asher",
"Aurora",
"Ava",
"Charlotte",
"Dana",
"Ellie",
"Emma",
"Ezra",
"Haley",
"Harper",
"Isabella",
"Luna",
"Mire",
"Olivia",
"Sophia",
};
static const char *const male[] =
{
"Oliver",
"Xavi",
"Benjamin",
"Elijah",
"Ethan",
"Jack",
"James",
"Jan",
"Kyte",
"Leo",
"Levi",
"Liam",
"Luca",
"Lucas",
"Michael",
"Noah",
};
switch (gender)
{
case FEMALE:
{
const size_t i = rand() % (sizeof female / sizeof *female);
return female[i];
}
case MALE:
{
const size_t i = rand() % (sizeof male / sizeof *male);
return male[i];
}
}
return NULL;
}
static const char *get_name(const enum unit_type type)
{
switch (type)
{
case UNIT_TYPE_1:
case UNIT_TYPE_2:
case UNIT_TYPE_3:
case UNIT_TYPE_4:
case UNIT_TYPE_5:
case UNIT_TYPE_6:
return get_walker_name(type);
case UNIT_TYPE_CAR_1:
case UNIT_TYPE_CAR_2:
return "Car";
case MAX_UNIT_TYPES:
break;
}
return NULL;
}
void unit_create(const struct unit_cfg *const cfg, struct unit *const u)
{
struct instance *const i = &u->instance;
enum unit_type type = 0;
switch (cfg->type)
{
case UNIT_CFG_TYPE_WALKER:
type = UNIT_TYPE_1 + (rand() % (UNIT_TYPE_6 + 1));
break;
case UNIT_CFG_TYPE_CAR:
type = UNIT_TYPE_CAR_1 + (rand()
% (UNIT_TYPE_CAR_2 - UNIT_TYPE_CAR_1 + 1));
break;
}
*u = (const struct unit)
{
.instance.alive = true,
.dir = UNIT_DIR_S,
.rx = fix16_from_int(cfg->x),
.ry = fix16_from_int(cfg->y),
.type = type,
.name = get_name(type)
};
get_dimensions(u->type, &i->r.w, &i->r.h);
unit_stop(u);
}