X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Flevel.c;fp=src%2Flevel.c;h=41208675c359d011acb68dbd424992faa64cb9b1;hb=d5f45e3128c537f272615cf76242e1dfebccdee7;hp=0000000000000000000000000000000000000000;hpb=dd7ce87b0ad2b8a1b4758bcc9354e993b71c8599;p=raydungeon diff --git a/src/level.c b/src/level.c new file mode 100644 index 0000000..4120867 --- /dev/null +++ b/src/level.c @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include "level.h" +#include "darray.h" +#include "assfile.h" +#include "util.h" + +static void parse_cell(struct level *lvl, int x, int y, char c); + +void init_level(struct level *lvl) +{ + memset(lvl, 0, sizeof *lvl); + + lvl->scale = 1.0f; + lvl->rects = darr_alloc(0, sizeof *lvl->rects); +} + +void destroy_level(struct level *lvl) +{ + darr_free(lvl->rects); +} + +static int rect_area(struct level_rect *rect) +{ + return rect->w * rect->h; +} + +static int grow_rect(struct level *lvl, struct level_cell *orgcell, struct level_rect *rect) +{ + int i, j, x, y, area_a, area_b; + struct level_rect ra, rb; + struct level_cell *cell; + + ra = rb = *rect; + + /* try expanding horizontally first */ + cell = orgcell + 1; + x = ra.x + 1; + while(x < lvl->xsz && cell->type != CELL_SOLID) { + ra.w++; + cell++; + } + /* then for each line below, expand as long as we have at least ra.w space */ + cell = orgcell + lvl->xsz; + y = ra.y + 1; + while(y++ < lvl->ysz) { + int rowsz = 0; + x = ra.x; + while(x++ < lvl->xsz && cell->type != CELL_SOLID) { + rowsz++; + cell++; + } + if(rowsz < ra.w) break; + ra.h++; + cell += lvl->xsz - rowsz; + } + + /* then try the same thing, but vertical first */ + cell = orgcell + lvl->xsz; + y = rb.y + 1; + while(y++ < lvl->ysz && cell->type != CELL_SOLID) { + rb.h++; + cell += lvl->xsz; + } + /* then for each next column, expand as long as we have at least rb.h space */ + x = rb.x + 1; + while(x < lvl->xsz) { + int colsz = 0; + y = rb.y; + cell = lvl->cells + y * lvl->xsz + x; + while(y++ < lvl->ysz && cell->type != CELL_SOLID) { + colsz++; + cell += lvl->xsz; + } + if(colsz < rb.h) break; + rb.w++; + x++; + } + + /* return the rect with the largest area */ + area_a = rect_area(&ra); + area_b = rect_area(&rb); + + *rect = area_a > area_b ? ra : rb; + + cell = orgcell; + for(i=0; ih; i++) { + for(j=0; jw; j++) { + cell[j].visited = 1; + } + cell += lvl->xsz; + } + return 0; +} + +void lvl_gen_rects(struct level *lvl) +{ + int i, j; + struct level_cell *cell; + struct level_rect rect; + + darr_clear(lvl->rects); + cell = lvl->cells; + for(i=0; iysz; i++) { + for(j=0; jxsz; j++) { + cell[j].visited = 0; + } + cell += lvl->xsz; + } + + cell = lvl->cells; + for(i=0; iysz; i++) { + for(j=0; jxsz; j++) { + if(cell->type != CELL_SOLID && !cell->visited) { + rect.x = j; + rect.y = i; + rect.w = rect.h = 1; + if(grow_rect(lvl, cell, &rect) != -1) { + rect.dbgcol[0] = (rand() & 0x7f) + 0x7f; + rect.dbgcol[1] = (rand() & 0x7f) + 0x7f; + rect.dbgcol[2] = (rand() & 0x7f) + 0x7f; + + darr_push(lvl->rects, &rect); + } + } + cell++; + } + } +} + +int save_level(const struct level *lvl, const char *fname) +{ + FILE *fp; + int i, j, num; + struct level_cell *cell; + struct level_rect *rect; + + if(!(fp = fopen(fname, "wb"))) { + fprintf(stderr, "save_level: failed to open: %s: %s\n", fname, strerror(errno)); + return -1; + } + + fprintf(fp, "RDLEVEL\n"); + fprintf(fp, "size = %dx%d\n", lvl->xsz, lvl->ysz); + fprintf(fp, "scale = %f\n", lvl->scale); + + fputs("\nMAPSTART\n", fp); + cell = lvl->cells; + for(i=0; iysz; i++) { + for(j=0; jxsz; j++) { + switch(cell->type & 0xff) { + case CELL_OPEN: + if(lvl->sx == j && lvl->sy == i) { + fputc('S', fp); + } else if((cell->type & CELL_WALK) == 0) { + fputc('.', fp); + } else { + fputc(' ', fp); + } + break; + + case CELL_SOLID: + default: + fputc('#', fp); + } + cell++; + } + fputc('\n', fp); + } + fputs("MAPEND\n", fp); + + if(!darr_empty(lvl->rects)) { + num = darr_size(lvl->rects); + fprintf(fp, "\nRECTSTART %d\n", num); + rect = lvl->rects; + for(i=0; ix, rect->y, rect->w, rect->h); + rect++; + } + fprintf(fp, "RECTEND\n\n"); + } + + fclose(fp); + return 0; +} + +static char *clean_line(char *s) +{ + char *end; + + while(*s && isspace(*s)) s++; + + if(!(end = strchr(s, '#'))) { + end = s + strlen(s) - 1; + } + while(end >= s && isspace(*end)) *end-- = 0; + + return *s ? s : 0; +} + +int load_level(struct level *lvl, const char *fname) +{ + ass_file *fp; + char buf[512]; + char *line, *val, *endp; + float fnum; + int state = 0; + int i, nrow; + struct level_rect rect; + + if(!(fp = ass_fopen(fname, "rb"))) { + fprintf(stderr, "load_level: failed to open: %s\n", fname); + return -1; + } + + while(ass_fgets(buf, sizeof buf, fp)) { + switch(state) { + case 0: + if(memcmp(buf, "RDLEVEL", 7) != 0) { + fprintf(stderr, "load_level: invalid level file: %s\n", fname); + ass_fclose(fp); + return -1; + } + state++; + break; + + case 1: + if(!(line = clean_line(buf)) || *line == '#') { + continue; + } + + if((val = strchr(line, '='))) { + *val++ = 0; + clean_line(line); + if(!(val = clean_line(val))) { + fprintf(stderr, "load_level: ignoring invalid value for %s: %s\n", line, val); + continue; + } + if(strcmp(line, "size") == 0) { + int x, y; + if(sscanf(val, "%dx%d", &x, &y) != 2) { + fprintf(stderr, "load_level: ignoring invalid size: %s\n", val); + continue; + } + lvl->xsz = x; + lvl->ysz = y; + } else if(strcmp(line, "scale") == 0) { + fnum = strtod(val, &endp); + if(endp == val) { + fprintf(stderr, "load_level: ignoring invalid scale: %s\n", val); + continue; + } + lvl->scale = fnum; + } else { + fprintf(stderr, "load_level: ignoring unknown option: %s\n", line); + continue; + } + + } else if(strcmp(line, "MAPSTART") == 0) { + if(!lvl->xsz || !lvl->ysz) { + fprintf(stderr, "load_level: missing size before level data\n"); + ass_fclose(fp); + return -1; + } + + lvl->cells = calloc_nf(lvl->xsz * lvl->ysz, sizeof *lvl->cells); + nrow = 0; + state++; + + } else if(sscanf(line, "rect %d %d %d %d", &rect.x, &rect.y, &rect.w, &rect.h) == 4) { + rect.dbgcol[0] = (rand() & 0x7f) + 0x7f; + rect.dbgcol[1] = (rand() & 0x7f) + 0x7f; + rect.dbgcol[2] = (rand() & 0x7f) + 0x7f; + darr_push(lvl->rects, &rect); + + } + break; + + case 2: + if(memcmp(buf, "MAPEND", 6) == 0) { + state = 1; + break; + } + for(i=0; ixsz; i++) { + if(!buf[i]) break; + parse_cell(lvl, i, nrow, buf[i]); + } + if(++nrow >= lvl->ysz) { + state = 1; + break; + } + break; + } + } + + ass_fclose(fp); + return 0; +} + +static void parse_cell(struct level *lvl, int x, int y, char c) +{ + struct level_cell *cell = lvl->cells + y * lvl->xsz + x; + + switch(c) { + case ' ': + cell->type = CELL_OPEN | CELL_WALK; + break; + + case 'S': + cell->type = CELL_OPEN | CELL_WALK; + lvl->sx = x; + lvl->sy = y; + break; + + case 't': + case '.': + cell->type = CELL_OPEN; + break; + + default: + break; + } +}