517 lines
13 KiB
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;
|
|
}
|