wall noise and arbitrary render resolution option
[raydungeon] / src / level.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <errno.h>
5 #include "level.h"
6 #include "darray.h"
7 #include "assfile.h"
8 #include "util.h"
9
10 static void parse_cell(struct level *lvl, int x, int y, char c);
11
12 void init_level(struct level *lvl)
13 {
14         memset(lvl, 0, sizeof *lvl);
15
16         lvl->scale = 1.0f;
17         lvl->rects = darr_alloc(0, sizeof *lvl->rects);
18 }
19
20 void destroy_level(struct level *lvl)
21 {
22         darr_free(lvl->rects);
23 }
24
25 static int rect_area(struct level_rect *rect)
26 {
27         return rect->w * rect->h;
28 }
29
30 static int grow_rect(struct level *lvl, struct level_cell *orgcell, struct level_rect *rect)
31 {
32         int i, j, x, y, area_a, area_b;
33         struct level_rect ra, rb;
34         struct level_cell *cell;
35
36         ra = rb = *rect;
37
38         /* try expanding horizontally first */
39         cell = orgcell + 1;
40         x = ra.x + 1;
41         while(x++ < lvl->xsz && cell->type == CELL_SOLID) {
42                 ra.w++;
43                 cell++;
44         }
45         /* then for each line below, expand as long as we have at least ra.w space */
46         cell = orgcell + lvl->xsz;
47         y = ra.y + 1;
48         while(y++ < lvl->ysz) {
49                 int rowsz = 0;
50                 x = ra.x;
51                 while(x++ < lvl->xsz && cell->type == CELL_SOLID) {
52                         rowsz++;
53                         cell++;
54                 }
55                 if(rowsz < ra.w) break;
56                 ra.h++;
57                 cell += lvl->xsz - rowsz;
58         }
59
60         /* then try the same thing, but vertical first */
61         cell = orgcell + lvl->xsz;
62         y = rb.y + 1;
63         while(y++ < lvl->ysz && cell->type == CELL_SOLID) {
64                 rb.h++;
65                 cell += lvl->xsz;
66         }
67         /* then for each next column, expand as long as we have at least rb.h space */
68         x = rb.x + 1;
69         while(x < lvl->xsz) {
70                 int colsz = 0;
71                 y = rb.y;
72                 cell = lvl->cells + y * lvl->xsz + x;
73                 while(y++ < lvl->ysz && cell->type == CELL_SOLID) {
74                         colsz++;
75                         cell += lvl->xsz;
76                 }
77                 if(colsz < rb.h) break;
78                 rb.w++;
79                 x++;
80         }
81
82         /* return the rect with the largest area */
83         area_a = rect_area(&ra);
84         area_b = rect_area(&rb);
85
86         *rect = area_a > area_b ? ra : rb;
87
88         cell = orgcell;
89         for(i=0; i<rect->h; i++) {
90                 for(j=0; j<rect->w; j++) {
91                         cell[j].visited = 1;
92                 }
93                 cell += lvl->xsz;
94         }
95         return 0;
96 }
97
98 void lvl_gen_rects(struct level *lvl)
99 {
100         int i, j;
101         struct level_cell *cell;
102         struct level_rect rect;
103
104         darr_clear(lvl->rects);
105         cell = lvl->cells;
106         for(i=0; i<lvl->ysz; i++) {
107                 for(j=0; j<lvl->xsz; j++) {
108                         cell[j].visited = 0;
109                 }
110                 cell += lvl->xsz;
111         }
112
113         cell = lvl->cells;
114         for(i=0; i<lvl->ysz; i++) {
115                 for(j=0; j<lvl->xsz; j++) {
116                         if(cell->type == CELL_SOLID && !cell->visited) {
117                                 rect.x = j;
118                                 rect.y = i;
119                                 rect.w = rect.h = 1;
120                                 if(grow_rect(lvl, cell, &rect) != -1) {
121                                         rect.dbgcol[0] = (rand() & 0x7f) + 0x7f;
122                                         rect.dbgcol[1] = (rand() & 0x7f) + 0x7f;
123                                         rect.dbgcol[2] = (rand() & 0x7f) + 0x7f;
124
125                                         darr_push(lvl->rects, &rect);
126                                 }
127                         }
128                         cell++;
129                 }
130         }
131 }
132
133 int save_level(const struct level *lvl, const char *fname)
134 {
135         FILE *fp;
136         int i, j, num;
137         struct level_cell *cell;
138         struct level_rect *rect;
139
140         if(!(fp = fopen(fname, "wb"))) {
141                 fprintf(stderr, "save_level: failed to open: %s: %s\n", fname, strerror(errno));
142                 return -1;
143         }
144
145         fprintf(fp, "RDLEVEL\n");
146         fprintf(fp, "size = %dx%d\n", lvl->xsz, lvl->ysz);
147         fprintf(fp, "scale = %f\n", lvl->scale);
148
149         fputs("\nMAPSTART\n", fp);
150         cell = lvl->cells;
151         for(i=0; i<lvl->ysz; i++) {
152                 for(j=0; j<lvl->xsz; j++) {
153                         switch(cell->type & 0xff) {
154                         case CELL_OPEN:
155                                 if(lvl->sx == j && lvl->sy == i) {
156                                         fputc('S', fp);
157                                 } else if((cell->type & CELL_WALK) == 0) {
158                                         fputc('.', fp);
159                                 } else {
160                                         fputc(' ', fp);
161                                 }
162                                 break;
163
164                         case CELL_SOLID:
165                         default:
166                                 fputc('#', fp);
167                         }
168                         cell++;
169                 }
170                 fputc('\n', fp);
171         }
172         fputs("MAPEND\n", fp);
173
174         if(!darr_empty(lvl->rects)) {
175                 num = darr_size(lvl->rects);
176                 fprintf(fp, "\nRECTSTART %d\n", num);
177                 rect = lvl->rects;
178                 for(i=0; i<num; i++) {
179                         fprintf(fp, "rect %d %d %d %d\n", rect->x, rect->y, rect->w, rect->h);
180                         rect++;
181                 }
182                 fprintf(fp, "RECTEND\n\n");
183
184
185                 fprintf(fp, "SDRSTART\n");
186                 fprintf(fp, "float eval_sdf_gen(in vec3 p)\n");
187                 fprintf(fp, "{\n");
188                 rect = lvl->rects;
189                 for(i=0; i<num; i++) {
190                         float cx = (rect->x - 0.5f + rect->w * 0.5f) * lvl->scale;
191                         float cy = (rect->y - 0.5f + rect->h * 0.5f) * lvl->scale;
192                         float rx = (rect->w + 0.1f) * lvl->scale * 0.5f;
193                         float ry = (rect->h + 0.1f) * lvl->scale * 0.5f;
194
195                         if(i == 0) {
196                                 fprintf(fp, "\tfloat d = boxdist(p - vec3(%f, 0.0, %f), vec3(%f, 1.0, %f));\n",
197                                                 cx, cy, rx, ry);
198                         } else {
199                                 fprintf(fp, "\td = min(d, boxdist(p - vec3(%f, 0.0, %f), vec3(%f, 1.0, %f)));\n",
200                                                 cx, cy, rx, ry);
201                         }
202                         rect++;
203                 }
204                 fprintf(fp, "\treturn d;\n}\n");
205                 fprintf(fp, "SDREND\n\n");
206         }
207
208         fclose(fp);
209         return 0;
210 }
211
212 static char *clean_line(char *s)
213 {
214         char *end;
215
216         while(*s && isspace(*s)) s++;
217
218         if(!(end = strchr(s, '#'))) {
219                 end = s + strlen(s) - 1;
220         }
221         while(end >= s && isspace(*end)) *end-- = 0;
222
223         return *s ? s : 0;
224 }
225
226 enum {
227         ST_HEADER,
228         ST_ASSIGN,
229         ST_MAP,
230         ST_SDR
231 };
232
233 int load_level(struct level *lvl, const char *fname)
234 {
235         ass_file *fp;
236         char buf[512];
237         char *line, *val, *endp;
238         float fnum;
239         int state = 0;
240         int i, nrow;
241         struct level_rect rect;
242         char *sdr = 0;
243
244         if(!(fp = ass_fopen(fname, "rb"))) {
245                 fprintf(stderr, "load_level: failed to open: %s\n", fname);
246                 return -1;
247         }
248
249         while(ass_fgets(buf, sizeof buf, fp)) {
250                 switch(state) {
251                 case ST_HEADER:
252                         if(memcmp(buf, "RDLEVEL", 7) != 0) {
253                                 fprintf(stderr, "load_level: invalid level file: %s\n", fname);
254                                 ass_fclose(fp);
255                                 return -1;
256                         }
257                         state = ST_ASSIGN;
258                         break;
259
260                 case ST_ASSIGN:
261                         if(!(line = clean_line(buf)) || *line == '#') {
262                                 continue;
263                         }
264
265                         if((val = strchr(line, '='))) {
266                                 *val++ = 0;
267                                 clean_line(line);
268                                 if(!(val = clean_line(val))) {
269                                         fprintf(stderr, "load_level: ignoring invalid value for %s: %s\n", line, val);
270                                         continue;
271                                 }
272                                 if(strcmp(line, "size") == 0) {
273                                         int x, y;
274                                         if(sscanf(val, "%dx%d", &x, &y) != 2) {
275                                                 fprintf(stderr, "load_level: ignoring invalid size: %s\n", val);
276                                                 continue;
277                                         }
278                                         lvl->xsz = x;
279                                         lvl->ysz = y;
280                                 } else if(strcmp(line, "scale") == 0) {
281                                         fnum = strtod(val, &endp);
282                                         if(endp == val) {
283                                                 fprintf(stderr, "load_level: ignoring invalid scale: %s\n", val);
284                                                 continue;
285                                         }
286                                         lvl->scale = fnum;
287                                 } else {
288                                         fprintf(stderr, "load_level: ignoring unknown option: %s\n", line);
289                                         continue;
290                                 }
291
292                         } else if(strcmp(line, "MAPSTART") == 0) {
293                                 if(!lvl->xsz || !lvl->ysz) {
294                                         fprintf(stderr, "load_level: missing size before level data\n");
295                                         ass_fclose(fp);
296                                         return -1;
297                                 }
298
299                                 lvl->cells = calloc_nf(lvl->xsz * lvl->ysz, sizeof *lvl->cells);
300                                 nrow = 0;
301                                 state = ST_MAP;
302
303                         } else if(strcmp(line, "SDRSTART") == 0) {
304                                 if(sdr) {
305                                         darr_clear(sdr);
306                                 } else {
307                                         sdr = darr_alloc(0, 1);
308                                 }
309                                 state = ST_SDR;
310
311                         } else if(sscanf(line, "rect %d %d %d %d", &rect.x, &rect.y, &rect.w, &rect.h) == 4) {
312                                 rect.dbgcol[0] = (rand() & 0x7f) + 0x7f;
313                                 rect.dbgcol[1] = (rand() & 0x7f) + 0x7f;
314                                 rect.dbgcol[2] = (rand() & 0x7f) + 0x7f;
315                                 darr_push(lvl->rects, &rect);
316
317                         }
318                         break;
319
320                 case ST_MAP:
321                         if(memcmp(buf, "MAPEND", 6) == 0) {
322                                 state = ST_ASSIGN;
323                                 break;
324                         }
325                         for(i=0; i<lvl->xsz; i++) {
326                                 if(!buf[i]) break;
327                                 parse_cell(lvl, i, nrow, buf[i]);
328                         }
329                         if(++nrow >= lvl->ysz) {
330                                 state = ST_ASSIGN;
331                                 break;
332                         }
333                         break;
334
335                 case ST_SDR:
336                         if(memcmp(buf, "SDREND", 6) == 0) {
337                                 state = ST_ASSIGN;
338                                 break;
339                         }
340                         for(i=0; buf[i]; i++) {
341                                 darr_strpush(sdr, buf[i]);
342                         }
343                         break;
344                 }
345         }
346
347         if(sdr) {
348                 lvl->sdf_src = darr_finalize(sdr);
349         }
350
351         ass_fclose(fp);
352         return 0;
353 }
354
355 static void parse_cell(struct level *lvl, int x, int y, char c)
356 {
357         struct level_cell *cell = lvl->cells + y * lvl->xsz + x;
358
359         switch(c) {
360         case ' ':
361                 cell->type = CELL_OPEN | CELL_WALK;
362                 break;
363
364         case 'S':
365                 cell->type = CELL_OPEN | CELL_WALK;
366                 lvl->sx = x;
367                 lvl->sy = y;
368                 break;
369
370         case 't':
371         case '.':
372                 cell->type = CELL_OPEN;
373                 break;
374
375         default:
376                 break;
377         }
378 }