jancity/src/terrain/src/terrain.c

238 lines
5.1 KiB
C

#include <terrain.h>
#include <camera.h>
#include <gfx.h>
#include <util.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>
struct sprite terrain_sprites[MAX_TERRAIN_SPRITES];
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;
}
}
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)
{
static const struct tile
{
const struct sprite *s;
int start, end;
} tiles[] =
{
{
.s = &terrain_sprites[ROOF1],
.start = ROOF1_START,
.end = ROOF1_END
}
};
const struct terrain_tile *const t = &map->m[y.i][x.i];
for (size_t i = 0; i < sizeof tiles / sizeof *tiles; i++)
{
const struct tile *const rt = &tiles[i];
if (t->t >= rt->start && t->t <= rt->end)
{
sprite_get_or_ret(s, -1);
if (sprite_clone(rt->s, s))
return -1;
s->x = x.p;
s->y = y.p;
sprite_sort(s);
return 0;
}
}
}
return -1;
}
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 *const 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, 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__, 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;
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;
}