jancity/src/player/src/human_player.c

517 lines
13 KiB
C

#include <human_player.h>
#include <human_player_private.h>
#include <player.h>
#include <building.h>
#include <camera.h>
#include <gfx.h>
#include <gui.h>
#include <instance.h>
#include <input.h>
#include <keyboard.h>
#include <pad.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, const bool excl)
{
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)
continue;
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)
{
struct sel_instance *sel = NULL;
if (excl)
{
sel = h->sel;
h->n_sel = 1;
for (size_t i = 1; i < sizeof h->sel / sizeof *h->sel; i++)
h->sel[i].d.u = NULL;
}
else
{
for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
{
struct sel_instance *const s = &h->sel[i];
if (!s->d.u)
{
sel = s;
h->n_sel++;
break;
}
}
}
if (sel)
{
sel->type = INSTANCE_TYPE_UNIT;
sel->d.u = u;
sfx_play(&unit_sounds[UNIT_SOUND_SELECTED]);
return true;
}
return false;
}
}
return false;
}
static bool select_buildings(struct human_player *const h, const short x,
const short y, const bool excl)
{
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)
{
struct sel_instance *sel = NULL;
if (excl)
{
sel = h->sel;
h->n_sel = 1;
for (size_t i = 1; i < sizeof h->sel / sizeof *h->sel; i++)
h->sel[i].d.b = NULL;
}
else
{
for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
{
struct sel_instance *const s = &h->sel[i];
if (!s->d.b)
{
sel = s;
h->n_sel++;
break;
}
}
}
if (sel)
{
sel->type = INSTANCE_TYPE_BUILDING;
sel->d.b = b;
return true;
}
return false;
}
}
}
return false;
}
static bool select_instances(struct human_player *const h,
const struct player_others *const o, const bool excl,
const bool same_type)
{
unsigned long x, y;
cursor_pos(&h->cam, &x, &y);
if (!same_type || !h->n_sel)
return select_buildings(h, x, y, excl)
|| select_units(h, x, y, excl);
else
{
switch (h->sel->type)
{
case INSTANCE_TYPE_UNIT:
return select_units(h, x, y, excl);
case INSTANCE_TYPE_BUILDING:
return select_buildings(h, x, y, excl);
}
}
return false;
}
static bool instance_collision(const struct camera *const cam,
const struct instance *const in)
{
return in->alive && cursor_collision(cam, &in->r);
}
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 void set_target(struct human_player *const h, struct unit *const u,
const struct unit_target *const t)
{
unit_set_target(u, t);
h->target = (const struct human_player_target)
{
.ins = t->ins,
.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))
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_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)
{
for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
h->sel[i] = (const struct sel_instance){0};
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 = 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)
h->target = (const struct human_player_target){0};
else if (++h->target.t >= TOGGLE)
{
enum {TIMES = 5};
h->target.render ^= true;
h->target.t = 0;
if (++h->target.n >= TIMES)
h->target = (const struct human_player_target){0};
}
}
}
static void update_from_pad(struct human_player *const h,
struct player_others *const o)
{
const struct pad *const p = &h->periph->pad.pad;
const struct input *const in = &h->in;
if (input_pad_justpressed(in, p, PAD_KEY_A))
select_instances(h, o, false, false);
else if (input_pad_justpressed(in, p, PAD_KEY_B))
move_units(h, o);
else if (input_pad_justpressed(in, p, PAD_KEY_C))
deselect_instances(h);
}
static void update_from_touch(struct human_player *const h,
struct player_others *const o)
{
const struct mouse *const m = &h->periph->kbm.mouse;
const struct input *const in = &h->in;
struct peripheral_kbm *const kbm = &h->periph->kbm;
bool *const pan = &h->cam.pan;
if (input_mouse_pressed(in, m, MOUSE_BUTTON_LEFT) && !*pan)
{
enum {LONG_PRESS_THRESHOLD = 30};
if (kbm->lp_t < LONG_PRESS_THRESHOLD)
kbm->lp_t++;
else if (!kbm->long_press)
{
kbm->long_press = true;
deselect_instances(h);
}
}
else if (input_mouse_justreleased(in, m, MOUSE_BUTTON_LEFT))
{
if (!*pan && !select_instances(h, o, false, true))
move_units(h, o);
*pan = false;
kbm->long_press = false;
kbm->lp_t = 0;
}
}
static bool hovering_units(const struct human_player *const h)
{
const 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 && cursor_collision(&h->cam, &in->r))
return true;
}
return false;
}
static void update_from_keyboard_mouse(struct human_player *const h,
struct player_others *const o)
{
struct mouse *const m = &h->periph->kbm.mouse;
const struct keyboard *const k = &h->periph->kbm.keyboard;
const struct input *const in = &h->in;
if (input_mouse_justreleased(in, m, MOUSE_BUTTON_LEFT))
{
const bool shift_pressed =
input_keyboard_pressed(in, k,
&KEYBOARD_COMBO(KEYBOARD_KEY_LSHIFT))
|| input_keyboard_pressed(in, k,
&KEYBOARD_COMBO(KEYBOARD_KEY_RSHIFT));
if (!select_instances(h, o, !shift_pressed, false))
deselect_instances(h);
}
else if (input_mouse_justreleased(in, m, MOUSE_BUTTON_RIGHT))
move_units(h, o);
else
m->hovering = hovering_units(h);
}
void human_player_update(struct human_player *const h,
struct player_others *const o)
{
struct player *const p = &h->pl;
if (p->alive)
{
human_player_gui_update(h);
update_selected(h);
update_target(h);
peripheral_update(h->periph);
input_update(&h->in, h->periph);
switch (h->periph->common.type)
{
case PERIPHERAL_TYPE_PAD:
update_from_pad(h, o);
break;
case PERIPHERAL_TYPE_TOUCH:
update_from_touch(h, o);
break;
case PERIPHERAL_TYPE_KEYBOARD_MOUSE:
update_from_keyboard_mouse(h, o);
break;
}
camera_update(&h->cam, h->periph, &h->in);
player_update(p);
}
}
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;
}
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)
|| human_player_gui_render(h)
|| input_render(&h->in, h->periph))
return -1;
switch (h->periph->common.type)
{
case PERIPHERAL_TYPE_PAD:
/* Fall through. */
case PERIPHERAL_TYPE_KEYBOARD_MOUSE:
cursor_render(&h->cam.cursor);
break;
case PERIPHERAL_TYPE_TOUCH:
break;
default:
return -1;
}
return 0;
}
int human_player_init(const struct human_player_cfg *const cfg,
struct human_player *const h)
{
*h = (const struct human_player){0};
if (player_init(&cfg->pl, &h->pl))
return -1;
cursor_init(&h->cam.cursor);
h->periph = cfg->p;
h->cam.dim = cfg->dim;
return 0;
}