Compare commits

...

3 Commits

56 changed files with 886 additions and 411 deletions

View File

@ -64,10 +64,161 @@ sprite(NAME btn_small
CY 48
TRANSPARENT TRUE)
sprite(NAME font
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME grass
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME unit1
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME unit2
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME unit3
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME unit4
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME unit5
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME unit6
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME sel_down_left
X 384
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME sel_down_right
X 384
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME sel_mid_down
X 384
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME sel_mid_top
X 384
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME sel_mid_v
X 384
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME sel_up_left
X 384
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME sel_up_right
X 384
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME checkbox
X 384
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME line_edit_left
X 0
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME line_edit_mid
X 0
Y 0
CX 0
CY 0
TRANSPARENT FALSE)
sprite(NAME line_edit_right
X 0
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
sprite(NAME car1
X 0
Y 0
CX 0
CY 0
TRANSPARENT TRUE)
level(NAME city1)
container(NAME jancity
SPRITES
car1
checkbox
grass
sidewalk
roof1
roof2
@ -76,4 +227,21 @@ container(NAME jancity
btn_mid
btn_right
btn_small
font
unit1
unit2
unit3
unit4
unit5
unit6
sel_down_left
sel_down_right
sel_mid_down
sel_mid_top
sel_mid_v
sel_up_left
sel_up_right
line_edit_left
line_edit_mid
line_edit_right
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/btn_small.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

BIN
res/btn_small_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
res/car1_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
res/checkbox_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
res/font.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
res/font_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -35,6 +35,7 @@ function(sprite)
VERBATIM)
add_custom_target(${SPRITE_NAME}_img
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${SPRITE_NAME})
add_dependencies(${PROJECT_NAME} ${SPRITE_NAME}_img)
add_dependencies(${SPRITE_NAME}_img tools)
endif()
endfunction()
@ -73,6 +74,7 @@ function(sound)
VERBATIM)
add_custom_target(${SOUND_NAME}_snd
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${SOUND_NAME})
add_dependencies(${PROJECT_NAME} ${SOUND_NAME}_snd)
add_dependencies(${SOUND_NAME}_snd tools)
endif()
endfunction()

BIN
res/line_edit_left_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/line_edit_mid_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/line_edit_right_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/sel_down_left_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

BIN
res/sel_down_right_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

BIN
res/sel_mid_down_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

BIN
res/sel_mid_top_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

BIN
res/sel_mid_v_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

BIN
res/sel_up_left_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

BIN
res/sel_up_right_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

BIN
res/unit1_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
res/unit2_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
res/unit3_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
res/unit4_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
res/unit5_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
res/unit6_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -33,7 +33,7 @@ void building_set_alive_cb(void (*f)(const struct util_rect *dim, bool alive, vo
int building_render(const struct building *b, const struct camera *cam, bool sel);
const char *building_str(const struct building *b);
extern struct sprite building_sprites[MAX_BUILDING_TYPES];
extern struct sprite building_sprites[1];
#ifdef __cplusplus
}

View File

@ -4,7 +4,7 @@
#include <stdbool.h>
#include <stddef.h>
struct sprite building_sprites[MAX_BUILDING_TYPES];
struct sprite building_sprites[1];
int building_render(const struct building *const b,
const struct camera *const cam, const bool sel)

View File

@ -28,6 +28,7 @@ struct camera
enum
{
CURSOR_STATE_IDLE,
CURSOR_STATE_HOVERING,
CURSOR_STATE_PRESSED
} state;
struct

View File

@ -55,9 +55,13 @@ int cursor_render(const struct cursor *const c)
case CURSOR_STATE_IDLE:
break;
case CURSOR_STATE_PRESSED:
case CURSOR_STATE_HOVERING:
s->u += CAMERA_CURSOR_WIDTH;
break;
case CURSOR_STATE_PRESSED:
s->u += CAMERA_CURSOR_WIDTH * 2;
break;
}
const int ret = sprite_sort(s);

View File

@ -13,9 +13,14 @@ static void cursor_update(struct cursor *const c, const struct mouse *const m)
c->x = m->x;
c->y = m->y;
c->state = mouse_pressed(m, MOUSE_BUTTON_LEFT) ||
mouse_pressed(m, MOUSE_BUTTON_RIGHT) ?
CURSOR_STATE_PRESSED: CURSOR_STATE_IDLE;
if (mouse_pressed(m, MOUSE_BUTTON_LEFT)
|| mouse_pressed(m, MOUSE_BUTTON_RIGHT))
c->state = CURSOR_STATE_PRESSED;
else if (m->hovering)
c->state = CURSOR_STATE_HOVERING;
else
c->state = CURSOR_STATE_IDLE;
}
static void update_speed(struct camera *const cam, const struct mouse *const m)
@ -24,8 +29,8 @@ static void update_speed(struct camera *const cam, const struct mouse *const m)
{
MAX_SPEED = 10,
STEP = 1,
THRESHOLD_X = CAMERA_CURSOR_WIDTH / 2,
THRESHOLD_Y = CAMERA_CURSOR_HEIGHT / 2
THRESHOLD_X = CAMERA_CURSOR_WIDTH,
THRESHOLD_Y = CAMERA_CURSOR_HEIGHT
};
const struct cursor *const c = &cam->cursor;

View File

@ -61,6 +61,126 @@ static const struct container c[] =
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_button_sprites[GUI_BUTTON_SMALL]
},
{
.path = "font",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &font_sprite
},
{
.path = "grass",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &terrain_sprites[GRASS]
},
{
.path = "unit1",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &unit_sprites[UNIT_1_SPRITE]
},
{
.path = "unit2",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &unit_sprites[UNIT_2_SPRITE]
},
{
.path = "unit3",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &unit_sprites[UNIT_3_SPRITE]
},
{
.path = "unit4",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &unit_sprites[UNIT_4_SPRITE]
},
{
.path = "unit5",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &unit_sprites[UNIT_5_SPRITE]
},
{
.path = "unit6",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &unit_sprites[UNIT_6_SPRITE]
},
{
.path = "sel_down_left",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_DOWN_LEFT]
},
{
.path = "sel_down_right",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_DOWN_RIGHT]
},
{
.path = "sel_mid_down",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID_DOWN]
},
{
.path = "sel_mid_top",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID_TOP]
},
{
.path = "sel_mid_v",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID_VERT]
},
{
.path = "sel_up_left",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_UP_LEFT]
},
{
.path = "sel_up_right",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_UP_RIGHT]
},
{
.path = "checkbox",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_checkbox_sprite
},
{
.path = "line_edit_left",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_line_edit_sprites[GUI_LINE_EDIT_LEFT]
},
{
.path = "line_edit_mid",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_line_edit_sprites[GUI_LINE_EDIT_MID]
},
{
.path = "line_edit_right",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &gui_line_edit_sprites[GUI_LINE_EDIT_RIGHT]
},
{
.path = "car1",
.type = CONTAINER_TYPE_SPRITE,
.data.sprite = &unit_sprites[UNIT_CAR_1_SPRITE]
}
};
static bool init;

View File

@ -15,6 +15,7 @@ struct gui_rounded_rect
{
struct gui_common common;
unsigned short w, h;
unsigned char r, g, b;
bool adjust;
};
@ -27,13 +28,15 @@ enum
GUI_ROUNDED_RECT_MID_VERT,
GUI_ROUNDED_RECT_DOWN_LEFT,
GUI_ROUNDED_RECT_DOWN_RIGHT,
GUI_ROUNDED_RECT_MID,
GUI_ROUNDED_RECT_MID_TOP,
GUI_ROUNDED_RECT_MID_DOWN,
MAX_GUI_ROUNDED_RECT_SPRITES
};
extern struct sprite gui_rounded_rect_sprites[MAX_GUI_ROUNDED_RECT_SPRITES];
/* Pad one line to avoid redefinition. */
UTIL_STATIC_ASSERT(!offsetof(struct gui_rounded_rect, common),
"unexpected offset for struct gui_rounded_rect");

View File

@ -134,30 +134,36 @@ static int render_rect(const struct gui_rounded_rect *const r,
rect_get_or_ret(sel, -1);
semitrans_rect_init(sel);
const struct sprite *const mid = &refs[GUI_ROUNDED_RECT_MID],
const struct sprite *const mid = &refs[GUI_ROUNDED_RECT_MID_TOP],
*const vert = &refs[GUI_ROUNDED_RECT_MID_VERT];
sel->x = x + vert->w;
sel->y = y + mid->h;
sel->w = r->w - (vert->w * 2);
sel->h = r->h - (mid->h * 2);
sel->r = 72;
sel->g = 66;
sel->b = 56;
sel->r = r->r;
sel->g = r->g;
sel->b = r->b;
rect_sort(sel);
return 0;
}
static int render_mid(const struct gui_rounded_rect *const r,
const short x, const short y)
const short x, const short y, const struct sprite *const s)
{
const short mid_w = refs[GUI_ROUNDED_RECT_MID].w,
const short mid_w = s->w,
top_w = refs[GUI_ROUNDED_RECT_UP_LEFT].w,
w = r->w - (top_w * 2),
rem_mid = w % mid_w,
whole_mid = w / mid_w,
n_mid = rem_mid ? whole_mid + 1 : whole_mid;
if (n_mid < 0)
{
fprintf(stderr, "%s: invalid dimensions\n", __func__);
return -1;
}
for (struct
{
size_t i;
@ -166,7 +172,7 @@ static int render_mid(const struct gui_rounded_rect *const r,
{
sprite_get_or_ret(m, -1);
if (sprite_clone(&refs[GUI_ROUNDED_RECT_MID], m))
if (sprite_clone(s, m))
return -1;
m->x = a.x;
@ -186,13 +192,15 @@ static int render_mid(const struct gui_rounded_rect *const r,
static int render_midtop(const struct gui_rounded_rect *const r,
const short x, const short y)
{
return render_mid(r, x, y);
return render_mid(r, x, y, &refs[GUI_ROUNDED_RECT_MID_TOP]);
}
static int render_middown(const struct gui_rounded_rect *const r,
const short x, const short y)
{
return render_mid(r, x, y + r->h - refs[GUI_ROUNDED_RECT_MID].h);
const struct sprite *const s = &refs[GUI_ROUNDED_RECT_MID_DOWN];
return render_mid(r, x, y + r->h - s->h, s);
}
static int render(const struct gui_common *const g)
@ -242,8 +250,8 @@ static void add_child(struct gui_common *const parent,
short w, h;
const short ref_w = refs[GUI_ROUNDED_RECT_MID_VERT].w,
ref_h = refs[GUI_ROUNDED_RECT_MID].h,
min_w = ref_w * 2 + refs[GUI_ROUNDED_RECT_MID].w,
ref_h = refs[GUI_ROUNDED_RECT_MID_TOP].h,
min_w = ref_w * 2 + refs[GUI_ROUNDED_RECT_MID_TOP].w,
min_h = ref_h * 2 + refs[GUI_ROUNDED_RECT_MID_VERT].h;
child->cb->get_dim(child, &w, &h);

View File

@ -1,3 +1,3 @@
add_library(instance "src/instance.c")
target_include_directories(instance PUBLIC "inc")
target_link_libraries(instance PUBLIC camera gfx util)
target_link_libraries(instance PUBLIC camera gfx util PRIVATE gui)

View File

@ -21,45 +21,30 @@ void instance_cyclic(void)
static int draw_sel(const struct instance *const i, const short x, const short y)
{
enum {R = 0, G = 255, B = 0};
rect_get_or_ret(r, -1);
stp_4line_get_or_ret(l, -1);
stp_4line_init(l);
l->x = l->vertices[2].x = l->vertices[3].x = x;
l->y = l->vertices[0].y = l->vertices[3].y = y;
l->vertices[0].x = l->vertices[1].x = x + i->r.w;
l->vertices[1].y = l->vertices[2].y = y + i->r.h;
l->r = R;
l->g = line_g;
l->b = B >> 2;
for (size_t i = 0; i < sizeof l->vertices / sizeof *l->vertices; i++)
{
struct stp_4line_vtx *const v = &l->vertices[i];
v->r = R;
v->b = B;
}
l->vertices[0].g = l->vertices[2].g = UCHAR_MAX - line_g;
l->vertices[1].g = l->vertices[3].g = line_g;
stp_4line_sort(l);
stp_4line_get_or_ret(ll, -1);
*ll = *l;
ll->x = ll->vertices[2].x = ll->vertices[3].x = l->x ? l->x - 1 : 0;
ll->y = ll->vertices[0].y = ll->vertices[3].y = l->y ? l->y - 1 : 0;
ll->vertices[0].x = ll->vertices[1].x = l->vertices[0].x + 1;
ll->vertices[1].y = ll->vertices[2].y = l->vertices[1].y + 1;
stp_4line_sort(ll);
return 0;
r->x = x;
r->y = y;
r->w = i->r.w;
r->h = i->r.h;
r->r = 10;
r->g = 137;
r->b = 225;
return rect_sort(r);
}
static void render_sprite(struct sprite *const s,
static int render_sprite(struct sprite *const s,
const struct instance_render_off *const off, const short x, const short y)
{
s->x = off ? x + off->x : x;
s->y = off ? y + off->y : y;
sprite_sort(s);
const int ret = sprite_sort(s);
if (ret)
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return ret;
}
static void render_quad(const struct instance_render_quad *const rq,

View File

@ -736,9 +736,10 @@ int menu_gamecfg(struct menu_common *const c, struct net_host *const h,
if (start)
{
struct game_cfg cfg =
const struct game_cfg cfg =
{
.p = &c->p
.p = &c->p,
.map = "city1.txt"
};
return game(&cfg);

View File

@ -71,12 +71,5 @@ int menu(void)
cursor_init(&c.cam.cursor);
peripheral_init(&cfg, &c.p);
settings_load("settings.ini", &c.s);
const struct game_cfg gcfg =
{
.p = &c.p,
.map = "city1.txt"
};
return game(&gcfg);
return menu_main(&c);
}

View File

@ -18,6 +18,7 @@ struct mouse
{
short x, y, dx, dy;
int mask, oldmask;
bool first_clicked, hovering;
};
void mouse_init(struct mouse *m);

View File

@ -3,13 +3,21 @@
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static void mouse_click(const SDL_MouseButtonEvent *const ev,
struct mouse *const m)
{
int mask;
if (!m->first_clicked)
{
srand(time(NULL));
m->first_clicked = true;
}
switch (ev->button)
{
case 1:

View File

@ -25,7 +25,7 @@ union peripheral
struct peripheral_common
{
enum peripheral_type type;
bool exit;
bool init, exit;
} common;
struct peripheral_pad

View File

@ -279,8 +279,7 @@ static void update_selected(struct human_player *const h)
{
struct sel_instance *const si = &h->sel[i];
if (si->d.i
&& (!si->d.i->alive || si->type == INSTANCE_TYPE_UNIT))
if (si->d.i && !si->d.i->alive)
{
si->d.i = NULL;
h->n_sel--;
@ -357,10 +356,26 @@ static void update_from_touch(struct human_player *const h,
}
}
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)
{
const struct mouse *const m = &h->periph->kbm.mouse;
struct mouse *const m = &h->periph->kbm.mouse;
const struct keyboard *const k = &h->periph->kbm.keyboard;
const struct input *const in = &h->in;
@ -377,6 +392,8 @@ static void update_from_keyboard_mouse(struct human_player *const 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,

View File

@ -41,14 +41,13 @@ static int render_sel_single_unit(const struct human_player *const h,
const struct sel_instance *const sel, struct gui_common *const r)
{
const struct unit *const u = sel->d.u;
enum {CARRY_X = 96, CARRY_Y = 8};
struct gui_label ul;
gui_label_init(&ul);
ul.common.x = X_OFF;
ul.common.y = Y_OFF;
ul.text = unit_str(u);
ul.common.hcentered = true;
ul.common.vcentered = true;
ul.text = u->name;
gui_add_child(r, &ul.common);
return gui_render(r);
@ -112,12 +111,18 @@ static int render_top(const struct human_player *const h)
static int render_sel(const struct human_player *const h)
{
const short wlim = 120;
struct gui_rounded_rect r;
short exp_w;
gui_rounded_rect_init(&r);
r.common.x = 16;
r.w = screen_w - (r.common.x * 2);
r.h = screen_h / 4;
r.common.hcentered = true;
exp_w = screen_w - 32;
r.w = exp_w > wlim ? wlim : exp_w;
r.h = 40;
r.r = 10;
r.g = 137;
r.b = 225;
r.common.y = screen_h - r.h;
return h->n_sel == 1 ? render_sel_single(h, &r.common)

View File

@ -61,6 +61,32 @@ int player_create_building(const struct building_cfg *const cfg, struct player *
int player_init(const struct player_cfg *const cfg, struct player *const pl)
{
struct unit_cfg ucfg =
{
.type = UNIT_CFG_TYPE_WALKER,
.x = cfg->x,
.y = cfg->y
};
if (player_create_unit(&ucfg, pl))
{
fprintf(stderr, "%s: player_create_unit failed\n", __func__);
return -1;
}
ucfg = (struct unit_cfg)
{
.type = UNIT_CFG_TYPE_CAR,
.x = cfg->x + 80,
.y = cfg->y + 80
};
if (player_create_unit(&ucfg, pl))
{
fprintf(stderr, "%s: player_create_unit failed\n", __func__);
return -1;
}
pl->alive = true;
pl->color = cfg->color;
pl->team = cfg->team;

View File

@ -3,5 +3,5 @@ add_library(terrain
"src/render.c"
"src/update.c"
)
target_include_directories(terrain PUBLIC "inc")
target_include_directories(terrain PUBLIC "inc" PRIVATE "privinc")
target_link_libraries(terrain PUBLIC container camera gfx util)

View File

@ -13,78 +13,12 @@ extern "C"
enum
{
MAP_TILES = 64,
MAP_TILES = 120,
TERRAIN_SZ = 16,
MAP_W = MAP_TILES * TERRAIN_SZ,
MAP_H = MAP_TILES * TERRAIN_SZ
};
enum terrain_type
{
ROOF1_1_NW,
ROOF1_1_N,
ROOF1_1_NE,
ROOF1_2_NW,
ROOF1_2_NE,
ROOF1_3_NW,
ROOF1_3_N,
ROOF1_3_NE,
ROOF1_4_N,
ROOF1_1_W,
ROOF1_1_C,
ROOF1_1_E,
ROOF1_2_SW,
ROOF1_2_SE,
ROOF1_3_SW,
ROOF1_3_S,
ROOF1_3_SE,
ROOF1_4_C,
ROOF1_1_SW,
ROOF1_1_S,
ROOF1_1_SE,
ROOF1_5_W,
ROOF1_5_C,
ROOF1_5_E,
ROOF1_6,
ROOF1_4_S,
ROOF2_1_NW,
ROOF2_1_N,
ROOF2_1_NE,
ROOF2_2_NW,
ROOF2_2_NE,
ROOF2_3_NW,
ROOF2_3_N,
ROOF2_3_NE,
ROOF2_4_N,
ROOF2_1_W,
ROOF2_1_C,
ROOF2_1_E,
ROOF2_2_SW,
ROOF2_2_SE,
ROOF2_3_SW,
ROOF2_3_S,
ROOF2_3_SE,
ROOF2_4_C,
ROOF2_1_SW,
ROOF2_1_S,
ROOF2_1_SE,
ROOF2_5_W,
ROOF2_5_C,
ROOF2_5_E,
ROOF2_6,
ROOF2_4_S,
ROOF1_START = ROOF1_1_NW,
ROOF1_END = ROOF1_4_S,
ROOF2_START = ROOF2_1_NW,
ROOF2_END = ROOF2_4_S
};
struct terrain_map
{
struct terrain_tile
@ -102,6 +36,7 @@ int terrain_render(const struct terrain_map *map, const struct camera *cam);
enum
{
SIDEWALK,
GRASS,
ROOF1,
ROOF2,
BUILDING1,

View File

@ -0,0 +1,153 @@
#ifndef TERRAIN_TILES_H
#define TERRAIN_TILES_H
#ifdef __cplusplus
extern "C"
{
#endif
enum
{
/* SIDEWALK */
SIDEWALK_1_NW,
SIDEWALK_1_N,
SIDEWALK_1_NE,
SIDEWALK_2_NW,
SIDEWALK_2_NE,
SIDEWALK_3_NW,
SIDEWALK_3_N,
SIDEWALK_3_NE,
SIDEWALK_4_N,
SIDEWALK_1_W,
SIDEWALK_1_C,
SIDEWALK_1_E,
SIDEWALK_2_SW,
SIDEWALK_2_SE,
SIDEWALK_3_SW,
SIDEWALK_3_S,
SIDEWALK_3_SE,
SIDEWALK_4_C,
SIDEWALK_1_SW,
SIDEWALK_1_S,
SIDEWALK_1_SE,
SIDEWALK_5_W,
SIDEWALK_5_C,
SIDEWALK_5_E,
SIDEWALK_6,
SIDEWALK_4_S,
SIDEWALK_START = SIDEWALK_1_NW,
SIDEWALK_END = SIDEWALK_4_S,
/* ROOF1 */
ROOF1_1_NW,
ROOF1_1_N,
ROOF1_1_NE,
ROOF1_2_NW,
ROOF1_2_NE,
ROOF1_3_NW,
ROOF1_3_N,
ROOF1_3_NE,
ROOF1_4_N,
ROOF1_1_W,
ROOF1_1_C,
ROOF1_1_E,
ROOF1_2_SW,
ROOF1_2_SE,
ROOF1_3_SW,
ROOF1_3_S,
ROOF1_3_SE,
ROOF1_4_C,
ROOF1_1_SW,
ROOF1_1_S,
ROOF1_1_SE,
ROOF1_5_W,
ROOF1_5_C,
ROOF1_5_E,
ROOF1_6,
ROOF1_4_S,
ROOF1_START = ROOF1_1_NW,
ROOF1_END = ROOF1_4_S,
/* ROOF2 */
ROOF2_1_NW,
ROOF2_1_N,
ROOF2_1_NE,
ROOF2_2_NW,
ROOF2_2_NE,
ROOF2_3_NW,
ROOF2_3_N,
ROOF2_3_NE,
ROOF2_4_N,
ROOF2_1_W,
ROOF2_1_C,
ROOF2_1_E,
ROOF2_2_SW,
ROOF2_2_SE,
ROOF2_3_SW,
ROOF2_3_S,
ROOF2_3_SE,
ROOF2_4_C,
ROOF2_1_SW,
ROOF2_1_S,
ROOF2_1_SE,
ROOF2_5_W,
ROOF2_5_C,
ROOF2_5_E,
ROOF2_6,
ROOF2_4_S,
ROOF2_START = ROOF2_1_NW,
ROOF2_END = ROOF2_4_S
};
enum
{
OBJECT_NONE,
/* GRASS */
GRASS_1_NW,
GRASS_1_N,
GRASS_1_NE,
GRASS_2_NW,
GRASS_2_NE,
GRASS_3_NW,
GRASS_3_N,
GRASS_3_NE,
GRASS_4_N,
GRASS_1_W,
GRASS_1_C,
GRASS_1_E,
GRASS_2_SW,
GRASS_2_SE,
GRASS_3_SW,
GRASS_3_S,
GRASS_3_SE,
GRASS_4_C,
GRASS_1_SW,
GRASS_1_S,
GRASS_1_SE,
GRASS_5_W,
GRASS_5_C,
GRASS_5_E,
GRASS_6,
GRASS_4_S,
GRASS_START = GRASS_1_NW,
GRASS_END = GRASS_4_S
};
#ifdef __cplusplus
}
#endif
#endif /* TERRAIN_TILES_H */

View File

@ -133,6 +133,8 @@ int terrain_init(const char *const path, struct terrain_map *const map)
FILE *const f = fopen(path, "rb");
unsigned w, h;
*map = (const struct terrain_map){0};
if (!f)
{
fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno));

View File

@ -1,4 +1,5 @@
#include <terrain.h>
#include <terrain_tiles.h>
#include <camera.h>
#include <gfx.h>
#include <util.h>
@ -12,33 +13,20 @@
struct sprite terrain_sprites[MAX_TERRAIN_SPRITES];
static int render_tile(const struct terrain_tile *const t, const short x,
const short y)
struct tile
{
static const struct tile
{
const struct sprite *s;
int start, end;
} tiles[] =
{
{
.s = &terrain_sprites[ROOF1],
.start = ROOF1_START,
.end = ROOF1_END
},
const struct sprite *s;
int start, end;
};
{
.s = &terrain_sprites[ROOF2],
.start = ROOF2_START,
.end = ROOF2_END
}
};
for (size_t i = 0; i < sizeof tiles / sizeof *tiles; i++)
static int render_tile(const unsigned char id, const short x,
const short y, const struct tile *const tiles, const size_t n)
{
for (size_t i = 0; i < n / n; i++)
{
const struct tile *const rt = &tiles[i];
if (t->t >= rt->start && t->t <= rt->end)
if (id >= rt->start && id <= rt->end)
{
sprite_get_or_ret(s, -1);
@ -48,7 +36,7 @@ static int render_tile(const struct terrain_tile *const t, const short x,
return -1;
}
const unsigned char pos = t->t - rt->start;
const unsigned char pos = id - rt->start;
const short tx = pos % TERRAIN_SZ, ty = pos / TERRAIN_SZ;
s->x = x;
@ -67,10 +55,41 @@ static int render_tile(const struct terrain_tile *const t, const short x,
}
}
fprintf(stderr, "%s: unknown tile %#hhx\n", __func__, t->t);
fprintf(stderr, "%s: unknown tile %#hhx\n", __func__, id);
return -1;
}
static int render_ground(const struct terrain_tile *const t, const short x,
const short y)
{
static const struct tile tiles[] =
{
#define TILE(t) {.s = &terrain_sprites[t], .start = t##_START, .end = t##_END}
TILE(SIDEWALK),
TILE(ROOF1),
TILE(ROOF2)
#undef TILE
};
return render_tile(t->t, x, y, tiles, sizeof tiles / sizeof *tiles);
}
static int render_object(const struct terrain_tile *const t, const short x,
const short y)
{
if (t->obj == OBJECT_NONE)
return 0;
static const struct tile tiles[] =
{
#define TILE(t) {.s = &terrain_sprites[t], .start = t##_START, .end = t##_END}
TILE(GRASS),
#undef TILE
};
return render_tile(t->obj, x, y, tiles, sizeof tiles / sizeof *tiles);
}
int terrain_render(const struct terrain_map *const map,
const struct camera *const cam)
{
@ -101,7 +120,8 @@ int terrain_render(const struct terrain_map *const map,
{
const struct terrain_tile *const t = &map->m[y.i][x.i];
if (render_tile(t, x.p, y.p))
if (render_ground(t, x.p, y.p)
|| render_object(t, x.p, y.p))
return -1;
}

View File

@ -33,17 +33,14 @@ struct unit
{
struct instance instance;
enum unit_type type;
const char *name;
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
UNIT_DIR_S,
UNIT_DIR_N,
UNIT_DIR_E
} dir;
enum unit_state state;
@ -61,7 +58,12 @@ UTIL_STATIC_ASSERT(!offsetof(struct unit, instance), "must be at offset zero");
struct unit_cfg
{
enum unit_type type;
enum
{
UNIT_CFG_TYPE_WALKER,
UNIT_CFG_TYPE_CAR
} type;
unsigned long x, y;
};
@ -72,20 +74,21 @@ 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);
void unit_update(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,
UNIT_1_SPRITE,
UNIT_2_SPRITE,
UNIT_3_SPRITE,
UNIT_4_SPRITE,
UNIT_5_SPRITE,
UNIT_6_SPRITE,
UNIT_CAR_1_SPRITE,
MAX_UNIT_SPRIES
MAX_UNIT_SPRITES
};
extern struct sprite unit_sprites[MAX_UNIT_SPRIES];
extern struct sprite unit_sprites[MAX_UNIT_SPRITES];
enum unit_sound
{

View File

@ -3,6 +3,14 @@
enum unit_type
{
UNIT_TYPE_1,
UNIT_TYPE_2,
UNIT_TYPE_3,
UNIT_TYPE_4,
UNIT_TYPE_5,
UNIT_TYPE_6,
UNIT_TYPE_CAR_1,
MAX_UNIT_TYPES
};

View File

@ -8,10 +8,10 @@
#include <stdlib.h>
#include <string.h>
struct sprite unit_sprites[MAX_UNIT_SPRIES];
struct sprite unit_sprites[MAX_UNIT_SPRITES];
struct sound unit_sounds[MAX_UNIT_SOUNDS];
enum {N_FRAMES = 5};
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)
{
@ -21,38 +21,18 @@ static void move_unit(struct unit *const u, const fix16_t sx, const fix16_t sy)
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;
}
@ -61,17 +41,50 @@ static void move_unit(struct unit *const u, const fix16_t sx, const fix16_t sy)
static void get_speed(const struct unit *const u, fix16_t *const x,
fix16_t *const y)
{
static const struct speed
struct speed
{
fix16_t x, y;
} speed[1];
} 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:
{
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 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;
*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)
@ -79,18 +92,7 @@ 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)
{
if (x > tx)
dir = UNIT_DIR_W;
@ -155,7 +157,7 @@ static bool must_move(const struct unit *const u)
void unit_update(struct unit *const u)
{
const struct instance *const i = &u->instance;
struct instance *const i = &u->instance;
if (!i->alive)
return;
@ -170,13 +172,13 @@ void unit_update(struct unit *const u)
get_speed(u, &x_step, &y_step);
move_unit(u, x_step, y_step);
enum {FRAME_RATE = 6};
enum {FRAME_RATE = 10};
if (++u->frame.t >= FRAME_RATE)
{
u->frame.t = 0;
if (++u->frame.i >= N_FRAMES)
if (++u->frame.i >= sizeof anim / sizeof *anim)
u->frame.i = 0;
}
@ -185,8 +187,8 @@ void unit_update(struct unit *const u)
else
u->frame.i = 0;
u->instance.r.x = fix16_to_int(u->rx);
u->instance.r.y = fix16_to_int(u->ry);
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)
@ -209,174 +211,58 @@ void unit_move_to(struct unit *const u, const unsigned long x, const unsigned lo
u->ty = y > y_off ? fix16_from_int(y - y_off) : 0;
}
static int get_ux(const struct unit *const u)
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_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;
s->w = s->h = 32;
s->u += 32;
off->x = -5;
break;
case UNIT_DIR_S:
return 4;
s->w = 16;
s->h = 32;
off->x = 2;
break;
default:
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;
}
return -1;
off->y = -10;
}
typedef const struct
static void adjust_sprite(const struct unit *const u, struct sprite *const s,
struct instance_render_off *const off)
{
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)
{
#if 0
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];
#else
return NULL;
#endif
}
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;
static anim_dim *(*const f[1])(const struct unit *);
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;
if (u->type >= UNIT_TYPE_1 && u->type <= UNIT_TYPE_6)
adjust_walker(u, s, off);
else if (u->type == UNIT_TYPE_CAR_1)
adjust_car(u, s, off);
}
int unit_render(const struct unit *const u, const struct camera *const cam,
@ -385,22 +271,26 @@ int unit_render(const struct unit *const u, const struct camera *const cam,
if (!u->instance.alive)
return 0;
struct render_cfg rcfg;
sprite_get_or_ret(s, -1);
quad_get_or_ret(q, -1);
rcfg.qcfg.q = q;
struct instance_render_off off;
if (unit_quad(u, &rcfg))
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_QUAD,
.prim = {.quad = &rcfg.qcfg},
.prim_type = INSTANCE_RENDER_CFG_SPRITE,
.prim.s = s,
.off = &off,
.cam = cam,
.sel = sel,
.off = &rcfg.off
.sel = sel
};
return instance_render(&cfg);
@ -409,40 +299,157 @@ int unit_render(const struct unit *const u, const struct camera *const cam,
static void get_dimensions(const enum unit_type type, short *const w,
short *const h)
{
static const struct dim
switch (type)
{
short w, h;
} dim[1];
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;
const struct dim *const d = &dim[type];
*w = d->w;
*h = d->h;
case UNIT_TYPE_CAR_1:
*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:
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_1 - UNIT_TYPE_CAR_1 + 1));
break;
}
*u = (const struct unit)
{
.instance =
{
.alive = true,
},
.type = cfg->type,
.instance.alive = true,
.dir = UNIT_DIR_S,
.rx = fix16_from_int(cfg->x),
.ry = fix16_from_int(cfg->y)
.ry = fix16_from_int(cfg->y),
.type = type,
.name = get_name(type)
};
get_dimensions(cfg->type, &i->r.w, &i->r.h);
get_dimensions(u->type, &i->r.w, &i->r.h);
unit_stop(u);
}
const char *unit_str(const struct unit *const u)
{
static const char *const str[1];
return str[u->type];
}