Compare commits

...

14 Commits

88 changed files with 742 additions and 1683 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
build/
build-*/
.vscode/
*.code-workspace
cdimg/

View File

@ -40,9 +40,6 @@ if(NOT PS1_BUILD)
find_package(ENET 1.3 REQUIRED)
endif()
set(cdroot ${CMAKE_BINARY_DIR}/cdimg)
file(MAKE_DIRECTORY ${cdroot})
if(PS1_BUILD)
include("cmake/ps1.cmake")
elseif(WIN9X_BUILD)

View File

@ -1,3 +1,5 @@
set(cdroot ${CMAKE_BINARY_DIR})
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(cflags ${cflags} -Og)
else()

View File

@ -1,5 +1,8 @@
find_package(PSXSDK 0.7.1 REQUIRED)
set(cdroot ${CMAKE_BINARY_DIR})
file(MAKE_DIRECTORY ${cdroot})
target_compile_definitions(${PROJECT_NAME} PUBLIC FIXMATH_FAST_SIN PSXSDK_DEBUG)
add_custom_target(exe ALL elf2exe ${PROJECT_NAME}
${cdroot}/${PROJECT_NAME}.exe -mark="A homebrew game created with PSXSDK"

View File

@ -1,3 +1,5 @@
set(cdroot ${CMAKE_BINARY_DIR})
add_custom_command(OUTPUT ${cdroot}/${PROJECT_NAME}.exe
COMMAND i386-mingw32-strip ${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}
-o ${cdroot}/${PROJECT_NAME}.exe

View File

@ -1 +1,79 @@
include(${CMAKE_CURRENT_LIST_DIR}/functions.cmake)
sprite(NAME sidewalk
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT FALSE)
sprite(NAME roof1
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT FALSE)
sprite(NAME roof2
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT FALSE)
sprite(NAME cursor
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME btn_left
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME btn_mid
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT FALSE)
sprite(NAME btn_right
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
sprite(NAME btn_small
X 384
Y 0
BPP 4
CX 384
CY 48
TRANSPARENT TRUE)
level(NAME city1)
container(NAME jancity
SPRITES
sidewalk
roof1
roof2
cursor
btn_left
btn_mid
btn_right
btn_small
)

BIN
res/btn_left.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

BIN
res/btn_left_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
res/btn_mid.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

BIN
res/btn_mid_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
res/btn_right.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

BIN
res/btn_right_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
res/building1_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
res/building2_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

21
res/city1.txt Normal file
View File

@ -0,0 +1,21 @@
24 16
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

BIN
res/cursor.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

BIN
res/cursor_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -77,6 +77,23 @@ function(sound)
endif()
endfunction()
function(level)
set(options "")
set(multiValueArgs "")
set(oneValueArgs NAME)
cmake_parse_arguments(LEVEL "${options}" "${oneValueArgs}"
"${multiValueArgs}" ${ARGN})
add_custom_command(OUTPUT ${cdroot}/${LEVEL_NAME}.txt
COMMAND cp ${LEVEL_NAME}.txt ${cdroot}/${LEVEL_NAME}.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${LEVEL_NAME}.txt
VERBATIM)
add_custom_target(${LEVEL_NAME}_lvl
DEPENDS ${cdroot}/${LEVEL_NAME}.txt)
add_dependencies(${PROJECT_NAME} ${LEVEL_NAME}_lvl)
endfunction()
function(container)
set(options "")
set(multiValueArgs SPRITES SOUNDS)

BIN
res/grass.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
res/grass_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
res/pavement.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
res/pavement_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
res/roof1.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
res/roof1_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
res/roof2.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
res/roof2_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
res/sidewalk.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
res/sidewalk_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
res/tileset1.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
res/tileset_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
res/water.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
res/water_24.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -20,7 +20,6 @@ set(components
pad
peripheral
player
resource
settings
sfx
system
@ -31,7 +30,6 @@ set(components
)
set(interfaces
tech
)
target_compile_options(${PROJECT_NAME} PUBLIC ${cflags})

View File

@ -31,7 +31,6 @@ struct building_cfg
void building_create(const struct building_cfg *cfg, struct building *b);
void building_set_alive_cb(void (*f)(const struct util_rect *dim, bool alive, void *p), void *p);
int building_render(const struct building *b, const struct camera *cam, bool sel);
instance_hp building_maxhp(const struct building *b);
const char *building_str(const struct building *b);
extern struct sprite building_sprites[MAX_BUILDING_TYPES];

View File

@ -3,8 +3,6 @@
enum building_type
{
BUILDING_TYPE_BARRACKS,
MAX_BUILDING_TYPES
};

View File

@ -6,16 +6,6 @@
struct sprite building_sprites[MAX_BUILDING_TYPES];
instance_hp building_maxhp(const struct building *const b)
{
static const instance_hp hp[] =
{
[BUILDING_TYPE_BARRACKS] = 100
};
return hp[b->type];
}
int building_render(const struct building *const b,
const struct camera *const cam, const bool sel)
{
@ -33,8 +23,7 @@ int building_render(const struct building *const b,
.prim_type = INSTANCE_RENDER_CFG_SPRITE,
.prim = {.s = s},
.cam = cam,
.sel = sel,
.max_hp = building_maxhp(b)
.sel = sel
};
return instance_render(&cfg);
@ -46,10 +35,7 @@ static void get_dimensions(const enum building_type type, short *const w,
static const struct dim
{
short w, h;
} dim[] =
{
[BUILDING_TYPE_BARRACKS] = {.w = 68, .h = 66}
};
} dim[1];
const struct dim *const d = &dim[type];
*w = d->w;
@ -69,7 +55,6 @@ void building_create(const struct building_cfg *const cfg,
i->r.x = cfg->x;
i->r.y = cfg->y;
i->alive = true;
i->hp = building_maxhp(b);
if (cb)
cb(&i->r, i->alive, op);
@ -84,10 +69,7 @@ void building_set_alive_cb(void (*const f)(const struct util_rect *, bool, void
const char *building_str(const struct building *const b)
{
static const char *const str[] =
{
[BUILDING_TYPE_BARRACKS] = "Barracks"
};
static const char *const str[1];
return str[b->type];
}

View File

@ -19,12 +19,12 @@ struct camera
} dim;
int x, y, x_speed, y_speed;
unsigned int xt, yt;
unsigned xt, yt;
bool pan;
struct cursor
{
unsigned int x, y, x_init, y_init;
unsigned x, y, x_init, y_init;
enum
{
CURSOR_STATE_IDLE,

View File

@ -13,8 +13,8 @@ extern "C"
enum
{
CAMERA_CURSOR_WIDTH = 20,
CAMERA_CURSOR_HEIGHT = 20
CAMERA_CURSOR_WIDTH = 16,
CAMERA_CURSOR_HEIGHT = 16
};
void camera_update_pos(struct camera *cam);

View File

@ -40,7 +40,10 @@ int cursor_render(const struct cursor *const c)
sprite_get_or_ret(s, -1);
if (sprite_clone(&cursor_sprite, s))
{
fprintf(stderr, "%s: sprite_clone failed\n", __func__);
return -1;
}
s->x = c->x;
s->y = c->y;
@ -57,13 +60,17 @@ int cursor_render(const struct cursor *const c)
break;
}
sprite_sort(s);
return 0;
const int ret = sprite_sort(s);
if (ret)
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return ret;
}
void cursor_init(struct cursor *const c)
{
const unsigned int x = (screen_w / 2) - CAMERA_CURSOR_WIDTH,
const unsigned x = (screen_w / 2) - CAMERA_CURSOR_WIDTH,
y = (screen_h / 2) - CAMERA_CURSOR_HEIGHT;
*c = (const struct cursor)

View File

@ -26,11 +26,6 @@ struct container
struct sprite *sprite;
struct sound *sound;
} data;
struct container_rt
{
struct container *c;
} *rt;
};
int container_load_ex(const char *path, const struct container *list, size_t n);

View File

@ -41,13 +41,19 @@ static int get_file_size(size_t *const sz, FILE *const f)
return -1;
errno = 0;
const unsigned long val = strtoul(szstr, NULL, 10);
char *end;
const unsigned long val = strtoul(szstr, &end, 10);
if (errno)
{
fprintf(stderr, "%s: strtoul(3): %s\n", __func__, strerror(errno));
return -1;
}
else if (*end)
{
fprintf(stderr, "%s: invalid number %s\n", __func__, szstr);
return -1;
}
*sz = val;
return 0;
@ -123,29 +129,24 @@ static const struct container *find_element(const struct container *const list,
static int read_element(const struct container *const list, const size_t n,
FILE *const f, bool *const done)
{
int ret = -1;
long init_off;
const struct container *el = NULL;
size_t sz;
if (!(el = find_element(list, n, f)))
goto end;
else if (get_file_size(&sz, f))
goto end;
if (!(el = find_element(list, n, f))
|| get_file_size(&sz, f))
return -1;
else if ((init_off = ftell(f)) < 0)
{
fprintf(stderr, "%s:%d: fseek failed: %s\n",
__func__, __LINE__, strerror(errno));
goto end;
return -1;
}
else if (read_file_contents(el, f, init_off, sz))
goto end;
return -1;
done[el - list] = true;
ret = 0;
end:
return ret;
return 0;
}
static int read_all_elements(const struct container *const list, const size_t n,

View File

@ -86,7 +86,12 @@ static int renderstr(const enum font f, const short x, short y,
s->v += v;
s->x = rx;
s->y = y;
sprite_sort(s);
if (sprite_sort(s))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
}
rx += cfg->fs;

View File

@ -9,7 +9,6 @@ target_link_libraries(game PUBLIC peripheral PRIVATE
instance
pad
player
resource
system
terrain
unit)

View File

@ -30,6 +30,7 @@ struct game_cfg
size_t n;
union peripheral *p;
const char *map;
};
int game_resinit(void);

View File

@ -3,7 +3,6 @@
#include <gfx.h>
#include <human_player.h>
#include <player.h>
#include <resource.h>
#include <system.h>
#include <terrain.h>
#include <stddef.h>
@ -15,8 +14,11 @@ int game(const struct game_cfg *const cfg)
struct human_player human;
struct terrain_map map;
terrain_init(&map);
building_set_alive_cb(terrain_block_update, &map);
if (terrain_init(cfg->map, &map))
{
fprintf(stderr, "%s: terrain_init failed\n", __func__);
return -1;
}
const struct human_player_cfg hcfg =
{
@ -38,30 +40,6 @@ int game(const struct game_cfg *const cfg)
if (human_player_init(&hcfg, &human))
goto end;
struct resource res[MAP_RESOURCES] = {0};
resource_set_alive_cb(terrain_block_update, &map);
if (resource_create(&(const struct resource_cfg)
{
.type = RESOURCE_TYPE_GOLD,
.x = 50,
.y = 200
}, res, sizeof res / sizeof *res)
|| resource_create(&(const struct resource_cfg)
{
.type = RESOURCE_TYPE_WOOD,
.x = 180,
.y = 200
}, res, sizeof res / sizeof *res)
|| resource_create(&(const struct resource_cfg)
{
.type = RESOURCE_TYPE_WOOD,
.x = 240,
.y = 200
}, res, sizeof res / sizeof *res))
goto end;
bool exit = false;
while (!exit)
@ -70,11 +48,7 @@ int game(const struct game_cfg *const cfg)
if (human.pl.alive)
{
struct player_others o =
{
.res = res,
.n_res = sizeof res / sizeof *res
};
struct player_others o;
human_player_update(&human, &o);
exit |= human.periph->common.exit;

View File

@ -8,7 +8,6 @@
#include <gui/line_edit.h>
#include <gui/rounded_rect.h>
#include <gui/checkbox.h>
#include <resource.h>
#include <terrain.h>
#include <unit.h>
#include <stdbool.h>
@ -16,274 +15,52 @@
static const struct container c[] =
{
{
.path = "barracks",
.path = "sidewalk",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &building_sprites[BUILDING_TYPE_BARRACKS]
}
.data.sprite = &terrain_sprites[SIDEWALK]
},
{
.path = "worker_n",
.path = "roof1",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &unit_sprites[UNIT_SPRITE_N]
}
.data.sprite = &terrain_sprites[ROOF1]
},
{
.path = "worker_ne",
.path = "roof2",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &unit_sprites[UNIT_SPRITE_NE]
}
},
{
.path = "worker_e",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &unit_sprites[UNIT_SPRITE_E]
}
},
{
.path = "worker_se",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &unit_sprites[UNIT_SPRITE_SE]
}
},
{
.path = "worker_s",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &unit_sprites[UNIT_SPRITE_S]
}
},
{
.path = "grass",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &grass_sprite
}
.data.sprite = &terrain_sprites[ROOF2]
},
{
.path = "cursor",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &cursor_sprite
}
},
{
.path = "gui_bar_left",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_bar_sprites[GUI_BAR_LEFT]
}
},
{
.path = "gui_bar_mid",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_bar_sprites[GUI_BAR_MID]
}
},
{
.path = "gui_bar_right",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_bar_sprites[GUI_BAR_RIGHT]
}
},
{
.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 = "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",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID]
}
},
{
.path = "sel_mid_v",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID_VERT]
}
},
{
.path = "font",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &font_sprite
}
},
{
.path = "gold_mine",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &resource_sprites[RESOURCE_TYPE_GOLD]
}
},
{
.path = "tree",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &resource_sprites[RESOURCE_TYPE_WOOD]
}
},
{
.path = "acknowledge_01",
.type = CONTAINER_TYPE_SOUND,
.data =
{
.sound = &unit_sounds[UNIT_SOUND_MOVE]
}
},
{
.path = "acknowledge_02",
.type = CONTAINER_TYPE_SOUND,
.data =
{
.sound = &unit_sounds[UNIT_SOUND_MOVE_2]
}
},
{
.path = "selected_01",
.type = CONTAINER_TYPE_SOUND,
.data =
{
.sound = &unit_sounds[UNIT_SOUND_SELECTED]
}
.data.sprite = &cursor_sprite
},
{
.path = "btn_left",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_button_sprites[GUI_BUTTON_LEFT]
}
.data.sprite = &gui_button_sprites[GUI_BUTTON_LEFT]
},
{
.path = "btn_mid",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_button_sprites[GUI_BUTTON_MID]
}
.data.sprite = &gui_button_sprites[GUI_BUTTON_MID]
},
{
.path = "btn_right",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_button_sprites[GUI_BUTTON_RIGHT]
}
.data.sprite = &gui_button_sprites[GUI_BUTTON_RIGHT]
},
{
.path = "line_edit_left",
.path = "btn_small",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_line_edit_sprites[GUI_LINE_EDIT_LEFT]
}
.data.sprite = &gui_button_sprites[GUI_BUTTON_SMALL]
},
{
.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 = "checkbox",
.type = CONTAINER_TYPE_SPRITE,
.data =
{
.sprite = &gui_checkbox_sprite
}
}
};
static bool init;
@ -301,7 +78,7 @@ int game_resinit(void)
{
if (!init)
{
if (container_load("rts.cnt", c, sizeof c / sizeof *c))
if (container_load("jancity.cnt", c, sizeof c / sizeof *c))
{
perror("container_load");
return -1;

View File

@ -17,14 +17,14 @@ int gfx_set_fullscreen(short w, short h);
bool gfx_fullscreen_available(void);
bool gfx_fullscreen(void);
int gfx_display_size(short *w, short *h);
void sprite_sort(struct sprite *s);
int sprite_sort(struct sprite *s);
int sprite_clone(const struct sprite *src, struct sprite *dst);
void rect_init(struct rect *r);
void semitrans_rect_init(struct rect *r);
void stp_4line_init(struct stp_4line *l);
void quad_sort(struct quad *q);
void rect_sort(struct rect *r);
void stp_4line_sort(struct stp_4line *l);
int quad_sort(struct quad *q);
int rect_sort(struct rect *r);
int stp_4line_sort(struct stp_4line *l);
int sprite_from_fp(struct sprite *s, FILE *f);
int quad_from_sprite(const struct sprite *s, struct quad *q);
bool gfx_inside_drawenv(short x, short y, short w, short h);

View File

@ -2,7 +2,7 @@
#include <gfx_private.h>
#include <stddef.h>
static unsigned int sel;
static unsigned sel;
static size_t heap_i;
void gfx_swapheap(void)

View File

@ -19,24 +19,28 @@ static void add_to_list(union gfx_sznext *const p)
last = p;
}
void sprite_sort(struct sprite *const s)
int sprite_sort(struct sprite *const s)
{
add_to_list(&s->sznext);
return 0;
}
void quad_sort(struct quad *const q)
int quad_sort(struct quad *const q)
{
add_to_list(&q->sznext);
return 0;
}
void rect_sort(struct rect *const r)
int rect_sort(struct rect *const r)
{
add_to_list(&r->sznext);
return 0;
}
void stp_4line_sort(struct stp_4line *const l)
int stp_4line_sort(struct stp_4line *const l)
{
add_to_list(&l->sznext);
return 0;
}
static void gfx_sync(void)
@ -61,7 +65,7 @@ int gfx_draw(void)
add_to_list(&term);
void gpu_ctrl(unsigned int command, unsigned int param);
void gpu_ctrl(unsigned command, unsigned param);
gfx_sync();
gfx_swapbuffers();

View File

@ -98,7 +98,10 @@ static int resize_screen(int w, int h, const bool full_screen)
for (size_t i = 0; i < list_len; i++)
if (sprite_screen_resize_ev(list[i]))
{
fprintf(stderr, "%s: sprite_screen_resize_ev failed\n", __func__);
return -1;
}
screen_w = w;
screen_h = h;
@ -152,14 +155,16 @@ static int get_resize_events(void)
const SDL_ResizeEvent *const res = &ev.resize;
if (resize_screen(res->w, res->h, false))
{
fprintf(stderr, "%s: resize_screen failed\n", __func__);
return -1;
}
}
}
if (n < 0)
{
fprintf(stderr, "%s: SDL_PeepEvents: %s\n",
__func__, SDL_GetError());
fprintf(stderr, "%s: SDL_PeepEvents: %s\n", __func__, SDL_GetError());
return -1;
}
@ -208,6 +213,5 @@ int gfx_draw(void)
return -1;
}
get_resize_events();
return 0;
return get_resize_events();
}

View File

@ -11,6 +11,7 @@ void semitrans_stp_4line_init(struct stp_4line *r)
{
}
void stp_4line_sort(struct stp_4line *const r)
int stp_4line_sort(struct stp_4line *const r)
{
return 0;
}

View File

@ -17,7 +17,7 @@ int quad_from_sprite(const struct sprite *const s, struct quad *const q)
return 0;
}
void quad_sort(struct quad *const q)
int quad_sort(struct quad *const q)
{
const bool xflip = q->x0 > q->x1;
@ -40,5 +40,10 @@ void quad_sort(struct quad *const q)
SDL_Surface *const s = xflip ? q->s_x : q->s;
if (SDL_BlitSurface(s, &clip, gfx_screen(), &r))
{
fprintf(stderr, "SDL_BlitSurface: %s\n", SDL_GetError());
return -1;
}
return 0;
}

View File

@ -3,7 +3,7 @@
#include <sdl-1.2/gfx_private.h>
#include <SDL.h>
void rect_sort(struct rect *const r)
int rect_sort(struct rect *const r)
{
SDL_Rect rct =
{
@ -19,8 +19,10 @@ void rect_sort(struct rect *const r)
if (SDL_FillRect(screen, &rct, map))
{
fprintf(stderr, "SDL_FillRect: %s\n", SDL_GetError());
return;
return -1;
}
return 0;
}
void rect_init(struct rect *const r)

View File

@ -126,7 +126,7 @@ int sprite_from_fp(struct sprite *const s, FILE *const f)
return 0;
}
void sprite_sort(struct sprite *const s)
int sprite_sort(struct sprite *const s)
{
SDL_Rect r =
{
@ -143,5 +143,10 @@ void sprite_sort(struct sprite *const s)
};
if (SDL_BlitSurface(s->s, &clip, gfx_screen(), &r))
{
fprintf(stderr, "SDL_BlitSurface: %s\n", SDL_GetError());
return -1;
}
return 0;
}

View File

@ -50,6 +50,7 @@ enum
GUI_BUTTON_LEFT,
GUI_BUTTON_MID,
GUI_BUTTON_RIGHT,
GUI_BUTTON_SMALL,
MAX_GUI_BUTTON_SPRITES
};

View File

@ -15,8 +15,13 @@ static int render_topleft(const struct gui_bar *const b, const short x,
s->x = x;
s->y = y;
sprite_sort(s);
return 0;
const int ret = sprite_sort(s);
if (ret)
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return ret;
}
static int render_topright(const struct gui_bar *const b, const short x,
@ -29,7 +34,12 @@ static int render_topright(const struct gui_bar *const b, const short x,
s->x = x + b->w - s->w;
s->y = y;
sprite_sort(s);
const int ret = sprite_sort(s);
if (ret)
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return 0;
}
@ -66,7 +76,11 @@ static int render_topmid(const struct gui_bar *const b, const short x,
else
m->w = mid_w;
sprite_sort(m);
if (sprite_sort(m))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
}
return 0;

View File

@ -13,8 +13,13 @@ int gui_button_render_sprite(const struct gui_button *const b)
return -1;
gui_coords(&b->common, &s->x, &s->y);
sprite_sort(s);
return 0;
const int ret = sprite_sort(s);
if (ret)
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return ret;
}
void gui_button_get_dim_sprite(const struct gui_button *const b,

View File

@ -19,7 +19,13 @@ static int render_left(const struct gui_button *const b,
s->x = *x;
s->y = y;
sprite_sort(s);
if (sprite_sort(s))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
*x = s->x + s->w;
return 0;
}
@ -65,7 +71,7 @@ static int render_mid(const struct gui_button *const b,
}
else
{
fprintf(stderr, "%s: unexpected negative size for button %p\n",
fprintf(stderr, "%s: unexpected zero or negative size for button %p\n",
__func__, (void *)b);
return -1;
}

View File

@ -26,7 +26,13 @@ static int render_left(const struct gui_line_edit *const l,
s->x = *x;
s->y = y;
sprite_sort(s);
if (sprite_sort(s))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
*x = s->x + s->w;
return 0;
}
@ -66,7 +72,12 @@ static int render_mid(const struct gui_line_edit *const l,
else
m->w = mid_w;
sprite_sort(m);
if (sprite_sort(m))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
*x += m->w;
}
}
@ -85,8 +96,13 @@ static int render_right(const short x, const short y)
s->x = x;
s->y = y;
sprite_sort(s);
return 0;
const int ret = sprite_sort(s);
if (ret)
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return ret;
}
static int render(const struct gui_common *const g)

View File

@ -19,7 +19,12 @@ static int render_top(const struct gui_rounded_rect *const r,
left->x = x;
left->y = y;
sprite_sort(left);
if (sprite_sort(left))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
}
{
@ -33,7 +38,12 @@ static int render_top(const struct gui_rounded_rect *const r,
right->x = x + r->w - ref->w;
right->y = y;
sprite_sort(right);
if (sprite_sort(right))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
}
return 0;

View File

@ -12,17 +12,12 @@ extern "C"
{
#endif
typedef unsigned int instance_hp;
struct instance
{
bool alive, dying;
unsigned int hp;
bool alive;
struct util_rect r;
};
typedef bool (*instance_attacked_cb)(struct instance *i, unsigned int ap);
typedef bool (*instance_sheltered_cb)(struct instance *self, struct instance *other);
typedef void (*instance_done_cb)(struct instance *i, void *op);
struct instance_render_cfg
@ -48,15 +43,12 @@ struct instance_render_cfg
} prim;
bool sel;
instance_hp max_hp;
const struct instance_render_off
{
short x, y;
} *off;
};
bool instance_attacked(struct instance *self, unsigned int ap);
void instance_clear_pools(void);
int instance_render(const struct instance_render_cfg *cfg);
int instance_render_target(const struct instance *const i, const struct camera *cam);
void instance_cyclic(void);

View File

@ -8,19 +8,6 @@
static unsigned char line_g;
static bool line_g_flip;
bool instance_attacked(struct instance *const self, const instance_hp ap)
{
if (self->hp > ap)
self->hp -= ap;
else
{
self->hp = 0;
self->alive = false;
}
return !self->alive;
}
void instance_cyclic(void)
{
if (!line_g_flip)

View File

@ -50,9 +50,6 @@ int menu_update(struct menu_common *const c,
case PERIPHERAL_TYPE_TOUCH:
break;
default:
return -1;
}
if (gfx_draw())
@ -75,5 +72,11 @@ int menu(void)
peripheral_init(&cfg, &c.p);
settings_load("settings.ini", &c.s);
return menu_main(&c);
const struct game_cfg gcfg =
{
.p = &c.p,
.map = "city1.txt"
};
return game(&gcfg);
}

View File

@ -40,7 +40,7 @@ union peripheral
struct mouse mouse;
struct keyboard keyboard;
bool long_press;
unsigned int lp_t;
unsigned lp_t;
} kbm;
};

View File

@ -14,8 +14,6 @@ target_link_libraries(player
input
mouse
pad
resource
tech
unit
util
PRIVATE

View File

@ -39,8 +39,7 @@ struct human_player
enum sel_type
{
INSTANCE_TYPE_UNIT,
INSTANCE_TYPE_BUILDING,
INSTANCE_TYPE_RESOURCE
INSTANCE_TYPE_BUILDING
} type;
union sel_data
@ -61,8 +60,6 @@ struct human_player
} target;
size_t n_sel;
bool top_gui;
uint32_t gui_res[MAX_RESOURCE_TYPES];
};
int human_player_init(const struct human_player_cfg *cfg, struct human_player *h);

View File

@ -5,8 +5,6 @@
#include <camera.h>
#include <instance.h>
#include <pad.h>
#include <resource.h>
#include <tech.h>
#include <unit.h>
#include <stdbool.h>
#include <stddef.h>
@ -16,7 +14,7 @@ extern "C"
{
#endif
typedef unsigned int player_team;
typedef unsigned player_team;
enum
{
@ -36,13 +34,7 @@ struct player
bool alive;
struct unit units[PLAYER_MAX_UNITS];
struct building buildings[PLAYER_MAX_BUILDINGS];
uint32_t resources[MAX_RESOURCE_TYPES];
unsigned char pop, bpop;
struct
{
struct unit_tech u;
} tree;
};
struct player_cfg
@ -55,8 +47,7 @@ struct player_cfg
struct player_others
{
struct player *pl;
struct resource *res;
size_t n_pl, n_res;
size_t n_pl;
};
int player_init(const struct player_cfg *cfg, struct player *pl);

View File

@ -9,7 +9,6 @@
#include <input.h>
#include <keyboard.h>
#include <pad.h>
#include <resource.h>
#include <unit.h>
#include <util.h>
#include <stdbool.h>
@ -42,61 +41,49 @@ static bool select_units(struct human_player *const h, const short x,
const struct unit *const u = &pl->units[i];
const struct instance *const in = &u->instance;
if (in->alive)
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)
{
switch (u->state)
struct sel_instance *sel = NULL;
if (excl)
{
default:
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++)
{
const union sel_data d = {.u = u};
struct sel_instance *const s = &h->sel[i];
if (!instance_selected(h, &d)
&& cursor_collision(&h->cam, &in->r)
&& h->n_sel < sizeof h->sel / sizeof *h->sel)
if (!s->d.u)
{
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;
sel = s;
h->n_sel++;
break;
}
}
break;
case UNIT_STATE_SHELTERED:
/* Fall through. */
case UNIT_STATE_HARVESTING_GOLD:
break;
}
if (sel)
{
sel->type = INSTANCE_TYPE_UNIT;
sel->d.u = u;
sfx_play(&unit_sounds[UNIT_SOUND_SELECTED]);
return true;
}
return false;
}
}
@ -161,62 +148,6 @@ static bool select_buildings(struct human_player *const h, const short x,
return false;
}
static bool select_resources(struct human_player *const h, const short x,
const short y, const struct player_others *const o, const bool excl)
{
for (size_t i = 0; i < o->n_res; i++)
{
const struct resource *const r = &o->res[i];
const struct instance *const in = &r->instance;
if (in->alive)
{
const union sel_data d = {.r = r};
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.r = 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.r)
{
sel = s;
h->n_sel++;
break;
}
}
}
if (sel)
{
sel->type = INSTANCE_TYPE_RESOURCE;
sel->d.r = r;
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)
@ -227,8 +158,7 @@ static bool select_instances(struct human_player *const h,
if (!same_type || !h->n_sel)
return select_buildings(h, x, y, excl)
|| select_units(h, x, y, excl)
|| select_resources(h, x, y, o, excl);
|| select_units(h, x, y, excl);
else
{
switch (h->sel->type)
@ -238,9 +168,6 @@ static bool select_instances(struct human_player *const h,
case INSTANCE_TYPE_BUILDING:
return select_buildings(h, x, y, excl);
case INSTANCE_TYPE_RESOURCE:
select_resources(h, x, y, o, excl);
}
}
@ -253,62 +180,6 @@ static bool instance_collision(const struct camera *const cam,
return in->alive && cursor_collision(cam, &in->r);
}
static void resources_stored(struct instance *const i, void *const op)
{
struct player *const p = op;
struct unit *const u = (struct unit *)i;
struct unit_harvester *const uh = &u->us.harvester;
p->resources[uh->type] += uh->carry;
uh->carry = 0;
unit_set_target(u, &uh->prev_target);
}
static void harvest_done(struct instance *const ins, void *const op)
{
struct player *const p = op;
struct unit *const u = (struct unit *)ins;
struct resource *const res = (struct resource *)u->target.ins;
if (res->type == RESOURCE_TYPE_GOLD)
{
struct resource_gold *const g = &res->res.gold;
for (size_t i = 0; i < sizeof g->miners / sizeof *g->miners; i++)
{
struct instance **const pi = &g->miners[i];
if (*pi == ins)
{
*pi = NULL;
g->n_miners--;
break;
}
}
}
for (size_t i = 0; i < sizeof p->buildings / sizeof *p->buildings; i++)
{
struct building *const b = &p->buildings[i];
if (b->instance.alive && b->type == BUILDING_TYPE_BARRACKS)
{
const struct unit_target t =
{
.ins = &b->instance,
.state = UNIT_STATE_CARRYING,
.done = resources_stored,
.op = p
/* TODO .state = UNIT_STATE_SHELTERED */
};
u->us.harvester.prev_target = u->target;
unit_set_target(u, &t);
break;
}
}
}
static bool target_from_own(struct player *const p,
const struct camera *const cam, struct unit_target *const t)
{
@ -332,101 +203,6 @@ static bool target_from_own(struct player *const p,
return false;
}
static bool target_from_others(const struct player *const p,
const struct player_others *const o,
const struct camera *const cam, struct unit_target *const t)
{
for (size_t i = 0; i < o->n_pl; i++)
{
struct player *const pl = &o->pl[i];
if (p->team != pl->team)
{
for (size_t i = 0; i < sizeof pl->units / sizeof *pl->units; i++)
{
struct unit *const u = &pl->units[i];
struct instance *const in = &u->instance;
if (instance_collision(cam, in))
{
*t = (const struct unit_target)
{
.ins = in,
.attack = unit_attacked,
.state = UNIT_STATE_ATTACKING
};
return true;
}
}
for (size_t i = 0; i < sizeof pl->buildings / sizeof *pl->buildings; i++)
{
struct building *const b = &pl->buildings[i];
struct instance *const in = &b->instance;
if (instance_collision(cam, in))
{
*t = (const struct unit_target)
{
.ins = in,
.state = UNIT_STATE_ATTACKING
};
return true;
}
}
}
}
return false;
}
static enum unit_state target_state_from_res(const struct resource *const res)
{
switch (res->type)
{
case RESOURCE_TYPE_GOLD:
return UNIT_STATE_HARVESTING_GOLD;
case RESOURCE_TYPE_WOOD:
return UNIT_STATE_HARVESTING_WOOD;
default:
break;
}
return UNIT_STATE_IDLE_MOVING;
}
static bool target_from_res(struct player *const p,
const struct player_others *const o,
const struct camera *const cam, struct unit_target *const t)
{
for (size_t i = 0; i < o->n_res; i++)
{
struct resource *const res = &o->res[i];
struct instance *const in = &res->instance;
if (instance_collision(cam, in))
{
*t = (const struct unit_target)
{
.ins = in,
.attack = resource_harvested,
.shelter = resource_shelter(res),
.state = target_state_from_res(res),
.done = harvest_done,
.op = p
};
return true;
}
}
return false;
}
static void set_target(struct human_player *const h, struct unit *const u,
const struct unit_target *const t)
{
@ -443,9 +219,7 @@ 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)
&& !target_from_others(p, o, cam, t)
&& !target_from_res(p, o, cam, t))
if (!target_from_own(p, cam, t))
t->ins = NULL;
}
@ -472,7 +246,7 @@ static void move_units(struct human_player *const h,
{
struct unit *const u = si->d.rw_u;
if (!t.ins || !unit_target_valid(u, &t))
if (!t.ins)
unit_move_to(u, x, y);
else
set_target(h, u, &t);
@ -505,10 +279,8 @@ 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->d.i->dying
|| (si->type == INSTANCE_TYPE_UNIT
&& (si->d.u->state == UNIT_STATE_HARVESTING_GOLD
|| si->d.u->state == UNIT_STATE_SHELTERED))))
if (si->d.i
&& (!si->d.i->alive || si->type == INSTANCE_TYPE_UNIT))
{
si->d.i = NULL;
h->n_sel--;
@ -552,8 +324,6 @@ static void update_from_pad(struct human_player *const h,
move_units(h, o);
else if (input_pad_justpressed(in, p, PAD_KEY_C))
deselect_instances(h);
else if (input_pad_justpressed(in, p, PAD_KEY_E))
h->top_gui ^= true;
}
static void update_from_touch(struct human_player *const h,
@ -686,29 +456,12 @@ static int render_own_buildings(const struct human_player *const h)
return 0;
}
static int render_resources(const struct human_player *const h,
const struct resource *const res, const size_t n)
{
for (size_t i = 0; i < n; i++)
{
const struct resource *const r = &res[i];
const union sel_data d = {.r = r};
const bool sel = instance_selected(h, &d);
if (resource_render(r, &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)
|| render_resources(h, o->res, o->n_res)
|| human_player_gui_render(h)
|| input_render(&h->in, h->periph))
return -1;
@ -742,9 +495,5 @@ int human_player_init(const struct human_player_cfg *const cfg,
cursor_init(&h->cam.cursor);
h->periph = cfg->p;
h->cam.dim = cfg->dim;
h->top_gui = true;
UTIL_STATIC_ASSERT(sizeof h->gui_res == sizeof h->pl.resources,
"unexpected sizeof for h->gui_res");
memmove(h->gui_res, h->pl.resources, sizeof h->gui_res);
return 0;
}

View File

@ -5,6 +5,7 @@
#include <unit.h>
#include <gui.h>
#include <gui/bar.h>
#include <gui/button.h>
#include <gui/container.h>
#include <gui/label.h>
#include <gui/progress_bar.h>
@ -17,76 +18,14 @@
enum {X_OFF = 8, Y_OFF = 8, HP_Y = 32};
static int draw_hp(const struct instance *const i, const instance_hp max_hp,
struct gui_common *const r)
{
enum {WIDTH = 64, HEIGHT = 4};
struct gui_progress_bar pb;
gui_progress_bar_init(&pb);
pb.common.x = X_OFF;
pb.common.y = HP_Y - 8;
pb.progress = ((unsigned)GUI_PROGRESS_BAR_MAX * i->hp) / max_hp;
pb.fg.g = UCHAR_MAX >> 1;
pb.bg.r = UCHAR_MAX >> 1;
pb.w = WIDTH;
pb.h = HEIGHT;
pb.stp = true;
gui_add_child(r, &pb.common);
return gui_render(r);
}
#if 0
static int draw_miners(const struct resource *const r)
{
const struct resource_gold *const g = &r->res.gold;
if (!g->n_miners)
return 0;
for (size_t i = 0, n = 0; i < sizeof g->miners / sizeof *g->miners; i++)
{
if (g->miners[i])
{
enum {OFFSET = 112, SZ = 16, GAP = 4};
rect_get_or_ret(r, -1);
semitrans_rect_init(r);
r->x = OFFSET + n * (SZ + GAP);
r->y = screen_h - 40;
r->r = r->g = r->b = 127;
r->w = r->h = SZ;
rect_sort(r);
if (++n >= g->n_miners)
break;
}
}
return 0;
}
#endif
void human_player_gui_update(struct human_player *const h)
{
struct player *const pl = &h->pl;
for (size_t i = 0; i < sizeof pl->resources / sizeof *pl->resources; i++)
{
if (h->gui_res[i] > pl->resources[i])
h->gui_res[i]--;
else if (h->gui_res[i] < pl->resources[i])
h->gui_res[i]++;
}
}
static int render_sel_single_building(const struct human_player *const h,
const struct sel_instance *const sel, struct gui_common *const r)
{
const struct building *const b = sel->d.b;
const struct instance *const in = &b->instance;
const instance_hp hp = in->hp, max_hp = building_maxhp(b);
struct gui_label bl;
gui_label_init(&bl);
@ -95,30 +34,13 @@ static int render_sel_single_building(const struct human_player *const h,
bl.text = building_str(b);
gui_add_child(r, &bl.common);
char hp_str[sizeof "65535/65535"];
const int rs = snprintf(hp_str, sizeof hp_str, "%u/%u", hp, max_hp);
if (rs < 0 || rs >= sizeof hp_str)
return -1;
struct gui_label hpl;
gui_label_init(&hpl);
hpl.common.x = X_OFF;
hpl.common.y = HP_Y;
hpl.text = hp_str;
gui_add_child(r, &hpl.common);
return draw_hp(in, max_hp, r);
return gui_render(r);
}
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;
const struct instance *const in = &u->instance;
const instance_hp hp = in->hp, max_hp = unit_maxhp(u);
enum {CARRY_X = 96, CARRY_Y = 8};
struct gui_label ul;
@ -129,75 +51,7 @@ static int render_sel_single_unit(const struct human_player *const h,
ul.text = unit_str(u);
gui_add_child(r, &ul.common);
char hp_str[sizeof "65535/65535"];
const int rs = snprintf(hp_str, sizeof hp_str, "%u/%u", hp, max_hp);
if (rs < 0 || rs >= sizeof hp_str)
return -1;
struct gui_label hpl;
gui_label_init(&hpl);
hpl.common.x = X_OFF;
hpl.common.y = HP_Y;
hpl.text = hp_str;
gui_add_child(r, &hpl.common);
char c_str[sizeof "255"];
struct gui_label cl;
if (unit_can_harvest(u))
{
const struct unit_harvester *const uh = &u->us.harvester;
if (uh->carry)
{
const int rs = snprintf(c_str, sizeof c_str, "%hhu", uh->carry);
if (rs < 0 || rs >= sizeof c_str)
return -1;
gui_label_init(&cl);
cl.common.x = CARRY_X;
cl.common.y = CARRY_Y;
cl.text = c_str;
gui_add_child(r, &cl.common);
}
}
return draw_hp(in, max_hp, r);
}
static int render_sel_single_resource(const struct human_player *const h,
const struct sel_instance *const sel, struct gui_common *const r)
{
const struct resource *const res = sel->d.r;
const struct instance *const in = &res->instance;
const instance_hp hp = in->hp, max_hp = resource_maxhp(res);
struct gui_label rl;
gui_label_init(&rl);
rl.common.x = X_OFF;
rl.common.y = Y_OFF;
rl.text = resource_str(res);
gui_add_child(r, &rl.common);
char hp_str[sizeof "65535/65535"];
const int rs = snprintf(hp_str, sizeof hp_str, "%u/%u", hp, max_hp);
if (rs < 0 || rs >= sizeof hp_str)
return -1;
struct gui_label hpl;
gui_label_init(&hpl);
hpl.common.x = X_OFF;
hpl.common.y = HP_Y;
hpl.text = hp_str;
gui_add_child(r, &hpl.common);
return draw_hp(in, max_hp, r);
return gui_render(r);
}
static int render_sel_single(const struct human_player *const h,
@ -216,14 +70,11 @@ static int render_sel_single(const struct human_player *const h,
case INSTANCE_TYPE_UNIT:
return render_sel_single_unit(h, sel, r);
case INSTANCE_TYPE_RESOURCE:
return render_sel_single_resource(h, sel, r);
}
}
}
/* Unreachable. */
fprintf(stderr, "%s: unreachable\n", __func__);
return -1;
}
@ -246,6 +97,19 @@ static int render_sel_multiple(const struct human_player *const h,
return gui_render(r);
}
static int render_top(const struct human_player *const h)
{
struct gui_button b;
gui_button_init(&b, GUI_BUTTON_TYPE_SPRITE);
b.on_pressed = NULL;
b.arg = NULL;
b.u.sprite.s = &gui_button_sprites[GUI_BUTTON_SMALL];
b.common.x = screen_w - b.u.sprite.s->w;
return gui_render(&b.common);
}
static int render_sel(const struct human_player *const h)
{
struct gui_rounded_rect r;
@ -256,90 +120,15 @@ static int render_sel(const struct human_player *const h)
r.h = screen_h / 4;
r.common.y = screen_h - r.h;
if (h->n_sel == 1)
return render_sel_single(h, &r.common);
return render_sel_multiple(h, &r.common);
}
static int render_top(const struct human_player *const h)
{
const struct player *const pl = &h->pl;
struct gui_container c;
struct gui_bar b;
gui_bar_init(&b);
b.w = screen_w;
gui_container_init(&c);
c.mode = GUI_CONTAINER_MODE_H;
c.spacing = 16;
c.common.hcentered = true;
c.common.vcentered = true;
gui_add_child(&b.common, &c.common);
char wood_str[sizeof "Wood=429496729"];
{
const int rs = snprintf(wood_str, sizeof wood_str,
"Wood=%" PRIu32, h->gui_res[RESOURCE_TYPE_WOOD]);
if (rs < 0 || rs >= sizeof wood_str)
return -1;
}
struct gui_label wl;
gui_label_init(&wl);
wl.common.vcentered = true;
wl.text = wood_str;
gui_add_child(&c.common, &wl.common);
char gold_str[sizeof "Gold=429496729"];
{
const int rs = snprintf(gold_str, sizeof gold_str,
"Gold=%" PRIu32, h->gui_res[RESOURCE_TYPE_GOLD]);
if (rs < 0 || rs >= sizeof wood_str)
return -1;
}
struct gui_label gl;
gui_label_init(&gl);
wl.common.vcentered = true;
gl.text = gold_str;
gui_add_child(&c.common, &gl.common);
char pop_str[sizeof "Pop.=255/255"];
{
const int rs = snprintf(pop_str, sizeof pop_str,
"Pop.=%hhu/%zu", pl->pop, sizeof pl->units / sizeof *pl->units);
if (rs < 0 || rs >= sizeof wood_str)
return -1;
}
struct gui_label popl;
gui_label_init(&popl);
wl.common.vcentered = true;
popl.text = pop_str;
gui_add_child(&c.common, &popl.common);
if (gui_render(&b.common))
return -1;
return 0;
return h->n_sel == 1 ? render_sel_single(h, &r.common)
: render_sel_multiple(h, &r.common);
}
int human_player_gui_render(const struct human_player *const h)
{
if (h->top_gui && render_top(h))
if (render_top(h))
return -1;
if (h->n_sel && render_sel(h))
else if (h->n_sel && render_sel(h))
return -1;
return 0;

View File

@ -9,7 +9,7 @@ void player_update(struct player *const p)
{
struct unit *const u = &p->units[i];
unit_update(&p->tree.u, u);
unit_update(u);
}
}
@ -61,50 +61,6 @@ 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)
{
const unsigned long x = cfg->x, y = cfg->y;
const struct building_cfg bcfg =
{
.type = BUILDING_TYPE_BARRACKS,
.x = x,
.y = y
};
const struct unit_cfg cfgs[] =
{
{
.type = UNIT_TYPE_PEASANT,
.x = x + 80,
.y = y
},
{
.type = UNIT_TYPE_PEASANT,
.x = x + 80,
.y = y + 40
},
{
.type = UNIT_TYPE_PEASANT,
.x = x + 100,
.y = y + 20
}
};
if (player_create_building(&bcfg, pl))
return -1;
for (size_t i = 0; i < sizeof cfgs / sizeof *cfgs; i++)
if (player_create_unit(&cfgs[i], pl))
return -1;
for (size_t i = 0; i < sizeof pl->resources / sizeof *pl->resources; i++)
{
enum {DEFAULT_RES = 120};
pl->resources[i] = DEFAULT_RES;
}
pl->alive = true;
pl->color = cfg->color;
pl->team = cfg->team;

View File

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

View File

@ -1,58 +0,0 @@
#ifndef RESOURCE_H
#define RESOURCE_H
#include <camera.h>
#include <container.h>
#include <gfx.h>
#include <instance.h>
#include <resource_type.h>
#include <util.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
enum {RESOURCE_GOLD_CAPACITY = 2};
struct resource
{
struct instance instance;
enum resource_type type;
union
{
struct resource_gold
{
struct instance *miners[RESOURCE_GOLD_CAPACITY];
size_t n_miners;
} gold;
} res;
};
UTIL_STATIC_ASSERT(!offsetof(struct resource, instance), "must be at offset zero");
struct resource_cfg
{
enum resource_type type;
unsigned long x, y;
};
const struct container_list *resource_res(void);
void resource_set_alive_cb(void (*f)(const struct util_rect *dim, bool alive, void *p), void *p);
int resource_create(const struct resource_cfg *cfg, struct resource *list, size_t n);
int resource_render(const struct resource *res, const struct camera *cam, bool sel);
instance_sheltered_cb resource_shelter(const struct resource *res);
bool resource_harvested(struct instance *i, instance_hp ap);
instance_hp resource_maxhp(const struct resource *res);
const char *resource_str(const struct resource *res);
extern struct sprite resource_sprites[MAX_RESOURCE_TYPES];
#ifdef __cplusplus
}
#endif
#endif /* RESOURCE_H */

View File

@ -1,21 +0,0 @@
#ifndef RESOURCE_TYPE_H
#define RESOURCE_TYPE_H
#ifdef __cplusplus
extern "C"
{
#endif
enum resource_type
{
RESOURCE_TYPE_WOOD,
RESOURCE_TYPE_GOLD,
MAX_RESOURCE_TYPES
};
#ifdef __cplusplus
}
#endif
#endif /* RESOURCE_TYPE_H */

View File

@ -1,177 +0,0 @@
#include <resource.h>
#include <gfx.h>
#include <instance.h>
#include <stdbool.h>
#include <stddef.h>
struct sprite resource_sprites[MAX_RESOURCE_TYPES];
static void (*cb)(const struct util_rect *, bool, void *);
static void *op;
static bool gold_shelter(struct instance *const self,
struct instance *const other)
{
struct resource *const res = (struct resource *)self;
struct resource_gold *const g = &res->res.gold;
for (size_t i = 0; i < sizeof g->miners / sizeof *g->miners; i++)
{
struct instance **const ins = &g->miners[i];
if (!*ins)
{
*ins = other;
g->n_miners++;
return true;
}
}
return false;
}
instance_sheltered_cb resource_shelter(const struct resource *res)
{
static const instance_sheltered_cb s[] =
{
[RESOURCE_TYPE_GOLD] = gold_shelter
};
return s[res->type];
}
bool resource_harvested(struct instance *const self, const instance_hp ap)
{
const bool ret = instance_attacked(self, ap);
if (ret && cb)
cb(&self->r, self->alive, op);
return ret;
}
instance_hp resource_maxhp(const struct resource *const res)
{
static const instance_hp hp[] =
{
[RESOURCE_TYPE_GOLD] = 1000,
[RESOURCE_TYPE_WOOD] = 45
};
return hp[res->type];
}
int resource_render(const struct resource *const res,
const struct camera *const cam, const bool sel)
{
const struct instance *const in = &res->instance;
if (!in->alive)
return 0;
sprite_get_or_ret(s, -1);
if (sprite_clone(&resource_sprites[res->type], s))
return -1;
const struct instance_render_off *off = NULL;
switch (res->type)
{
case RESOURCE_TYPE_GOLD:
s->h = in->r.h;
if (res->res.gold.n_miners)
s->v += s->h;
break;
case RESOURCE_TYPE_WOOD:
{
static const struct instance_render_off w_off =
{
.y = -30
};
off = &w_off;
}
break;
default:
break;
}
const struct instance_render_cfg cfg =
{
.i = &res->instance,
.prim_type = INSTANCE_RENDER_CFG_SPRITE,
.prim = {.s = s},
.cam = cam,
.sel = sel,
.max_hp = resource_maxhp(res),
.off = off
};
return instance_render(&cfg);
}
static void get_dimensions(const enum resource_type type, short *const w,
short *const h)
{
static const struct dim
{
short w, h;
} dim[] =
{
[RESOURCE_TYPE_GOLD] = {.w = 96, .h = 96},
[RESOURCE_TYPE_WOOD] = {.w = 32, .h = 16}
};
const struct dim *const d = &dim[type];
*w = d->w;
*h = d->h;
}
int resource_create(const struct resource_cfg *const cfg, struct resource *const list,
const size_t n)
{
for (size_t i = 0; i < n; i++)
{
struct resource *const r = &list[i];
struct instance *const in = &r->instance;
if (!in->alive)
{
get_dimensions(cfg->type, &in->r.w, &in->r.h);
r->type = cfg->type;
in->r.x = cfg->x;
in->r.y = cfg->y;
in->alive = true;
in->hp = resource_maxhp(r);
if (cb)
cb(&in->r, in->alive, op);
return 0;
}
}
return -1;
}
void resource_set_alive_cb(void (*const f)(const struct util_rect *, bool, void *),
void *const p)
{
cb = f;
op = p;
}
const char *resource_str(const struct resource *const res)
{
static const char *const str[] =
{
[RESOURCE_TYPE_GOLD] = "Gold mine",
[RESOURCE_TYPE_WOOD] = "Pine tree"
};
return str[res->type];
}

View File

@ -10,6 +10,7 @@ elseif(SDL1_2_BUILD)
set(inc ${inc} "sdl-1.2/inc")
set(privinc ${privinc} "sdl-1.2/privinc")
set(privdeps ${privdeps} SDL::SDL)
set(privdefs ${privdefs} "PROJECT_NAME=\"${PROJECT_NAME}\"")
if(WIN9X_BUILD)
set(src ${src} "win9x/src/system.c")
@ -21,3 +22,4 @@ endif()
add_library(system ${src})
target_include_directories(system PUBLIC ${inc} PRIVATE ${privinc})
target_link_libraries(system PRIVATE ${privdeps})
target_compile_definitions(system PRIVATE ${privdefs})

View File

@ -37,7 +37,7 @@ int system_init(void)
else if (gfx_init() || sfx_init() || net_init())
goto failure;
SDL_WM_SetCaption("rts", NULL);
SDL_WM_SetCaption(PROJECT_NAME, NULL);
SDL_ShowCursor(0);
/* SDL_WM_GrabInput(SDL_GRAB_ON); */
SDL_EnableUNICODE(1);

View File

@ -1,2 +0,0 @@
add_library(tech INTERFACE)
target_include_directories(tech INTERFACE "inc")

View File

@ -1,20 +0,0 @@
#ifndef TECH_H
#define TECH_H
#ifdef __cplusplus
extern "C"
{
#endif
enum tech_level
{
TECH_LEVEL_1,
TECH_LEVEL_2,
TECH_LEVEL_3
};
#ifdef __cplusplus
}
#endif
#endif /* TECH_H */

View File

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

View File

@ -13,34 +13,37 @@ extern "C"
enum
{
MAP_TILES = 64,
TERRAIN_SZ = 32,
MAP_TILES = 120,
TERRAIN_SZ = 16,
MAP_W = MAP_TILES * TERRAIN_SZ,
MAP_H = MAP_TILES * TERRAIN_SZ
};
enum terrain_type
{
TERRAIN_TYPE_GRASS
};
struct terrain_map
{
struct terrain_tile
{
enum terrain_type t;
unsigned char bl;
unsigned char t, obj;
} m[MAP_TILES][MAP_TILES];
int nx, ny, last_w, last_h;
};
void terrain_init(struct terrain_map *map);
int terrain_init(const char *path, struct terrain_map *map);
void terrain_update(struct terrain_map *map);
int terrain_render(const struct terrain_map *map, const struct camera *cam);
void terrain_block_update(const struct util_rect *dim, bool alive, void *p);
extern struct sprite grass_sprite;
enum
{
SIDEWALK,
ROOF1,
ROOF2,
BUILDING1,
MAX_TERRAIN_SPRITES
};
extern struct sprite terrain_sprites[MAX_TERRAIN_SPRITES];
#ifdef __cplusplus
}

154
src/terrain/src/init.c Normal file
View File

@ -0,0 +1,154 @@
#include <terrain.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int read_token(FILE *const f, char *const s, const size_t n)
{
uint8_t c;
size_t i = 0;
while (fread(&c, sizeof c, 1, f))
{
if (isspace(c))
{
if (i)
break;
else
continue;
}
else if (!isalnum(c))
{
fprintf(stderr, "%s: invalid character %" PRIx8 " at offset %ld\n",
__func__, c, ftell(f));
return -1;
}
else if (i + 1 >= n)
{
fprintf(stderr, "%s: exceeded maximum token size %zu\n",
__func__, n);
return -1;
}
s[i++] = c;
}
if (!i)
{
fprintf(stderr, "%s: feof=%d, ferror=%d\n", __func__, feof(f),
ferror(f));
return -1;
}
s[i] = '\0';
return 0;
}
static int read_num(FILE *const f, char *s, const size_t n,
unsigned *const out, const int base, const int limit)
{
if (read_token(f, s, n))
return -1;
errno = 0;
char *end;
const unsigned long value = strtoul(s, &end, base);
if (errno)
{
fprintf(stderr, "%s: strtoul(3): %s\n", __func__, strerror(errno));
return -1;
}
else if (*end)
{
fprintf(stderr, "%s: invalid number %s\n", __func__, s);
return -1;
}
else if (value > limit)
{
fprintf(stderr, "%s: %lu exceeds maximum range %d\n", __func__, value,
limit);
return -1;
}
*out = value;
return 0;
}
static int read_dec(FILE *const f, unsigned *const out)
{
char s[sizeof "65535"];
return read_num(f, s, sizeof s, out, 10, UINT16_MAX);
}
static int read_hex(FILE *const f, unsigned *const out)
{
char s[sizeof "0000"];
return read_num(f, s, sizeof s, out, 16, UINT16_MAX);
}
static int read_dimensions(FILE *const f, unsigned *const w, unsigned *const h)
{
if (read_dec(f, w) || read_dec(f, h))
return -1;
else if (*w > MAP_TILES || *h > MAP_TILES)
{
fprintf(stderr, "%s: map size (%ux%u) exceeded, max %dx%d\n",
__func__, *w, *h, MAP_TILES, MAP_TILES);
return -1;
}
return 0;
}
static int read_map(FILE *const f, const unsigned w, const unsigned h,
struct terrain_map *const map)
{
for (unsigned j = 0; j < h; j++)
for (unsigned i = 0; i < w; i++)
{
unsigned tile;
if (read_hex(f, &tile))
return -1;
map->m[j][i].t = tile;
map->m[j][i].obj = tile >> 8;
}
return 0;
}
int terrain_init(const char *const path, struct terrain_map *const map)
{
int ret = -1;
FILE *const f = fopen(path, "rb");
unsigned w, h;
if (!f)
{
fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno));
goto end;
}
else if (read_dimensions(f, &w, &h) || read_map(f, w, h, map))
goto end;
ret = 0;
end:
if (f && fclose(f))
{
fprintf(stderr, "%s: fclose(3): %s\n", __func__, strerror(errno));
ret = -1;
}
return ret;
}

129
src/terrain/src/render.c Normal file
View File

@ -0,0 +1,129 @@
#include <terrain.h>
#include <terrain_tiles.h>
#include <camera.h>
#include <gfx.h>
#include <util.h>
#include <ctype.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct sprite terrain_sprites[MAX_TERRAIN_SPRITES];
struct tile
{
const struct sprite *s;
int start, end;
};
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 (id >= rt->start && id <= rt->end)
{
sprite_get_or_ret(s, -1);
if (sprite_clone(rt->s, s))
{
fprintf(stderr, "%s: sprite_clone failed\n", __func__);
return -1;
}
const unsigned char pos = id - rt->start;
const short tx = pos % TERRAIN_SZ, ty = pos / TERRAIN_SZ;
s->x = x;
s->y = y;
s->w = TERRAIN_SZ;
s->h = TERRAIN_SZ;
s->u = tx * TERRAIN_SZ;
s->v = ty * TERRAIN_SZ;
const int ret = sprite_sort(s);
if (ret)
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return ret;
}
}
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)
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)
{
const int start_x = abs(cam->x / TERRAIN_SZ),
start_y = abs(cam->y / TERRAIN_SZ);
const int remx = cam->x % TERRAIN_SZ,
remy = cam->y % TERRAIN_SZ;
int nx = map->nx, ny = map->ny;
if (abs(remx))
nx++;
if (abs(remy))
ny++;
struct m
{
size_t i;
long p;
};
for (struct m x = {.i = start_x, .p = remx};
x.i < nx + start_x; x.i++, x.p += TERRAIN_SZ)
for (struct m y = {.i = start_y, .p = remy};
y.i < ny + start_y; y.i++, y.p += TERRAIN_SZ)
{
const struct terrain_tile *const t = &map->m[y.i][x.i];
if (render_ground(t, x.p, y.p)
|| render_object(t, x.p, y.p))
return -1;
}
return 0;
}

View File

@ -1,132 +0,0 @@
#include <terrain.h>
#include <camera.h>
#include <gfx.h>
#include <util.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
struct sprite grass_sprite;
void terrain_update(struct terrain_map *const map)
{
if (map->last_w != screen_w)
{
const int extra = !!(screen_w % TERRAIN_SZ);
map->nx = screen_w / TERRAIN_SZ + extra;
map->last_w = screen_w;
}
if (map->last_h != screen_h)
{
const int extra = !!(screen_h % TERRAIN_SZ);
map->ny = screen_h / TERRAIN_SZ + extra;
map->last_h = screen_h;
}
}
struct block
{
size_t x0, x1, y0, y1;
};
void terrain_block_update(const struct util_rect *const dim, const bool alive,
void *const p)
{
const struct block b =
{
.x0 = dim->x / TERRAIN_SZ,
.x1 = (dim->x + dim->w) / TERRAIN_SZ,
.y0 = dim->y / TERRAIN_SZ,
.y1 = (dim->y + dim->h) / TERRAIN_SZ
};
for (size_t x = b.x0; x <= b.x1; x++)
for (size_t y = b.y0; y <= b.y1; y++)
for (size_t i = 0; i < 2; i++)
for (size_t j = 0; j < 2; j++)
{
const struct util_rect sub =
{
.x = x * TERRAIN_SZ + i * (TERRAIN_SZ / 2),
.y = y * TERRAIN_SZ + j * (TERRAIN_SZ / 2),
.w = TERRAIN_SZ / 2,
.h = TERRAIN_SZ / 2
};
if (util_collision(dim, &sub))
{
const enum
{
NO_SUB = 0,
UPPER_LEFT = 1 << 0,
UPPER_RIGHT = 1 << 1,
LOWER_LEFT = 1 << 2,
LOWER_RIGHT = 1 << 3,
ALL_BLOCKS = UPPER_LEFT | UPPER_RIGHT
| LOWER_LEFT | LOWER_RIGHT
} sub = 1 << (i + j * 2);
struct terrain_map *const map = p;
unsigned char *const bl = &map->m[y][x].bl;
*bl = alive ? *bl | sub : *bl & ~sub;
}
}
}
int terrain_render(const struct terrain_map *const map,
const struct camera *const cam)
{
const int start_x = abs(cam->x / TERRAIN_SZ),
start_y = abs(cam->y / TERRAIN_SZ);
const int remx = cam->x % TERRAIN_SZ,
remy = cam->y % TERRAIN_SZ;
int nx = map->nx, ny = map->ny;
if (abs(remx))
nx++;
if (abs(remy))
ny++;
struct m
{
size_t i;
long p;
};
for (struct m x = {.i = start_x, .p = remx};
x.i < nx + start_x; x.i++, x.p += TERRAIN_SZ)
for (struct m y = {.i = start_y, .p = remy};
y.i < ny + start_y; y.i++, y.p += TERRAIN_SZ)
{
const struct terrain_tile *const t = &map->m[y.i][x.i];
sprite_get_or_ret(s, -1);
switch (t->t)
{
case TERRAIN_TYPE_GRASS:
if (sprite_clone(&grass_sprite, s))
return -1;
break;
}
s->x = x.p;
s->y = y.p;
sprite_sort(s);
}
return 0;
}
void terrain_init(struct terrain_map *const map)
{
*map = (const struct terrain_map){0};
}

20
src/terrain/src/update.c Normal file
View File

@ -0,0 +1,20 @@
#include <terrain.h>
void terrain_update(struct terrain_map *const map)
{
if (map->last_w != screen_w)
{
const int extra = !!(screen_w % TERRAIN_SZ);
map->nx = screen_w / TERRAIN_SZ + extra;
map->last_w = screen_w;
}
if (map->last_h != screen_h)
{
const int extra = !!(screen_h % TERRAIN_SZ);
map->ny = screen_h / TERRAIN_SZ + extra;
map->last_h = screen_h;
}
}

View File

@ -1,3 +1,12 @@
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)
target_link_libraries(unit
PUBLIC
fixmath
container
camera
gfx
instance
sfx
util
)

View File

@ -5,8 +5,6 @@
#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>
@ -18,27 +16,15 @@ 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
UNIT_STATE_IDLE_MOVING
};
struct unit_target
{
struct instance *ins;
enum unit_state state;
instance_sheltered_cb shelter;
instance_attacked_cb attack;
instance_done_cb done;
void *op;
};
@ -67,18 +53,7 @@ struct unit
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;
};
@ -96,9 +71,7 @@ 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);
void unit_update(struct unit *u);
const char *unit_str(const struct unit *u);
enum

View File

@ -3,8 +3,6 @@
enum unit_type
{
UNIT_TYPE_PEASANT,
MAX_UNIT_TYPES
};

View File

@ -64,14 +64,7 @@ static void get_speed(const struct unit *const u, fix16_t *const x,
static const struct speed
{
fix16_t x, y;
} speed[] =
{
[UNIT_TYPE_PEASANT] =
{
.x = FIX16_C_FROM_INT(1),
.y = FIX16_C_FROM_INT(1)
}
};
} speed[1];
const struct speed *const s = &speed[u->type];
const int dx = abs(u->rx - u->tx);
@ -115,16 +108,6 @@ static enum unit_dir get_direction(const struct unit *const u)
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;
@ -151,68 +134,18 @@ static void target_interact(struct unit *const u)
const struct instance *const ins = t->ins;
/* TODO: lose u->ti if not visible. */
if (ins->alive)
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);
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)
@ -220,164 +153,40 @@ 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)
void unit_update(struct unit *const u)
{
const struct instance *const i = &u->instance;
if (i->alive)
{
if (u->target.ins)
target_interact(u);
if (!i->alive)
return;
switch (u->state)
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 = 6};
if (++u->frame.t >= FRAME_RATE)
{
case UNIT_STATE_SHELTERED:
break;
u->frame.t = 0;
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;
if (++u->frame.i >= N_FRAMES)
u->frame.i = 0;
}
u->state = UNIT_STATE_IDLE_MOVING;
}
}
else
u->frame.i = 0;
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;
u->instance.r.x = fix16_to_int(u->rx);
u->instance.r.y = fix16_to_int(u->ry);
}
void unit_set_target(struct unit *const u, const struct unit_target *const t)
@ -400,16 +209,6 @@ 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;
}
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)
@ -452,6 +251,7 @@ typedef const struct
static anim_dim *peasant_anim(const struct unit *const u)
{
#if 0
static anim_dim t[] =
{
{
@ -506,6 +306,9 @@ static anim_dim *peasant_anim(const struct unit *const u)
return NULL;
return &t[ux];
#else
return NULL;
#endif
}
struct render_cfg
@ -561,10 +364,7 @@ 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[])(const struct unit *) =
{
[UNIT_TYPE_PEASANT] = peasant_quad
};
static anim_dim *(*const f[1])(const struct unit *);
anim_dim *const dim = f[u->type](u);
@ -582,9 +382,7 @@ static int unit_quad(const struct unit *const u, struct render_cfg *const rcfg)
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)
if (!u->instance.alive)
return 0;
struct render_cfg rcfg;
@ -602,7 +400,6 @@ int unit_render(const struct unit *const u, const struct camera *const cam,
.prim = {.quad = &rcfg.qcfg},
.cam = cam,
.sel = sel,
.max_hp = unit_maxhp(u),
.off = &rcfg.off
};
@ -615,10 +412,7 @@ static void get_dimensions(const enum unit_type type, short *const w,
static const struct dim
{
short w, h;
} dim[] =
{
[UNIT_TYPE_PEASANT] = {.w = 36, .h = 36}
};
} dim[1];
const struct dim *const d = &dim[type];
*w = d->w;
@ -634,7 +428,6 @@ void unit_create(const struct unit_cfg *const cfg, struct unit *const u)
.instance =
{
.alive = true,
.hp = unit_maxhp(u)
},
.type = cfg->type,
@ -649,10 +442,7 @@ void unit_create(const struct unit_cfg *const cfg, struct unit *const u)
const char *unit_str(const struct unit *const u)
{
static const char *const str[] =
{
[UNIT_TYPE_PEASANT] = "Peasant"
};
static const char *const str[1];
return str[u->type];
}