Support large maps up to 8192x8192 tiles

Raise hard map size cap from 4096 to 8192 via MAX_MAP_SIZE constant.
Replace fixed 16 KB fgets buffer with dynamic line reader that handles
arbitrarily long tile rows. Check calloc returns for tile layer
allocation.

Add entity distance culling: skip updates for entities beyond 2x screen
width from camera, skip rendering beyond 64 px margin. Dead entities
bypass culling so cleanup callbacks always run. Player, spacecraft,
drone, and moving platforms set ENTITY_ALWAYS_UPDATE to opt out.

Replace naive 4-neighbor flood fill with scanline algorithm (O(height)
stack instead of O(area)) for safe use on large maps.

Raise MAX_ENTITY_SPAWNS from 128 to 512 and MAX_EXIT_ZONES from 8 to
16 to support populated large levels.
This commit is contained in:
Thomas
2026-03-01 15:17:58 +00:00
parent ad2d68a8b4
commit df3bc29fb7
11 changed files with 181 additions and 45 deletions

View File

@@ -6,6 +6,33 @@
#include <stdlib.h>
#include <string.h>
/* Read a full line from f into a dynamically growing buffer.
* *buf and *cap track the heap buffer; the caller must free *buf.
* Returns the line length, or -1 on EOF/error. */
static int read_line(FILE *f, char **buf, int *cap) {
if (!*buf) {
*cap = 16384;
*buf = malloc(*cap);
if (!*buf) return -1;
}
int len = 0;
for (;;) {
if (len + 1 >= *cap) {
int new_cap = *cap * 2;
char *tmp = realloc(*buf, new_cap);
if (!tmp) return -1;
*buf = tmp;
*cap = new_cap;
}
int ch = fgetc(f);
if (ch == EOF) return len > 0 ? len : -1;
(*buf)[len++] = (char)ch;
if (ch == '\n') break;
}
(*buf)[len] = '\0';
return len;
}
bool tilemap_load(Tilemap *map, const char *path, SDL_Renderer *renderer) {
FILE *f = fopen(path, "r");
if (!f) {
@@ -15,12 +42,13 @@ bool tilemap_load(Tilemap *map, const char *path, SDL_Renderer *renderer) {
memset(map, 0, sizeof(Tilemap));
char line[16384];
char tileset_path[256] = {0};
int current_layer = -1; /* 0=collision, 1=bg, 2=fg */
int row = 0;
char *line = NULL;
int line_cap = 0;
char tileset_path[256] = {0};
int current_layer = -1; /* 0=collision, 1=bg, 2=fg */
int row = 0;
while (fgets(line, sizeof(line), f)) {
while (read_line(f, &line, &line_cap) >= 0) {
/* Skip comments and empty lines */
if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') continue;
@@ -30,8 +58,9 @@ bool tilemap_load(Tilemap *map, const char *path, SDL_Renderer *renderer) {
} else if (strncmp(line, "SIZE ", 5) == 0) {
sscanf(line + 5, "%d %d", &map->width, &map->height);
if (map->width <= 0 || map->height <= 0 ||
map->width > 4096 || map->height > 4096) {
map->width > MAX_MAP_SIZE || map->height > MAX_MAP_SIZE) {
fprintf(stderr, "Invalid map size: %dx%d\n", map->width, map->height);
free(line);
fclose(f);
return false;
}
@@ -39,6 +68,17 @@ bool tilemap_load(Tilemap *map, const char *path, SDL_Renderer *renderer) {
map->collision_layer = calloc(total, sizeof(uint16_t));
map->bg_layer = calloc(total, sizeof(uint16_t));
map->fg_layer = calloc(total, sizeof(uint16_t));
if (!map->collision_layer || !map->bg_layer || !map->fg_layer) {
fprintf(stderr, "Failed to allocate tile layers (%dx%d)\n",
map->width, map->height);
free(map->collision_layer);
free(map->bg_layer);
free(map->fg_layer);
memset(map, 0, sizeof(Tilemap));
free(line);
fclose(f);
return false;
}
} else if (strncmp(line, "SPAWN ", 6) == 0) {
float sx, sy;
sscanf(line + 6, "%f %f", &sx, &sy);
@@ -132,6 +172,7 @@ bool tilemap_load(Tilemap *map, const char *path, SDL_Renderer *renderer) {
}
}
}
free(line);
fclose(f);
/* Load tileset texture */