visibility determination
[vrlugburz] / src / level.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <treestore.h>
6 #include "level.h"
7 #include "tileset.h"
8 #include "fs.h"
9
10 static int detect_cell_tile(struct level *lvl, int x, int y, int *rot);
11
12
13 int init_level(struct level *lvl, int xsz, int ysz)
14 {
15         int i, j;
16         struct cell *cell;
17
18         memset(lvl, 0, sizeof *lvl);
19
20         if(!(lvl->cells = calloc(xsz * ysz, sizeof *lvl->cells))) {
21                 free(lvl);
22                 return -1;
23         }
24         lvl->width = xsz;
25         lvl->height = ysz;
26         lvl->cell_size = DEF_CELL_SIZE;
27         lvl->px = lvl->py = -1;
28
29         lvl->visdist = 4;
30
31         /* assign cell coordinates */
32         cell = lvl->cells;
33         for(i=0; i<ysz; i++) {
34                 for(j=0; j<xsz; j++) {
35                         cell->x = j;
36                         cell->y = i;
37                         cell++;
38                 }
39         }
40         return 0;
41 }
42
43 void destroy_level(struct level *lvl)
44 {
45         if(!lvl) return;
46         free(lvl->cells);
47         free(lvl->fname);
48         free(lvl->dirname);
49 }
50
51 int load_level(struct level *lvl, const char *fname)
52 {
53         struct ts_node *ts, *node, *iter;
54         int i, j, sz, cx, cy, tiletype;
55         struct cell *cell;
56         float *vecptr;
57         const char *str;
58         char *tset_path;
59
60         if(!(ts = ts_load(fname))) {
61                 fprintf(stderr, "failed to load level: %s\n", fname);
62                 return -1;
63         }
64         if(strcmp(ts->name, "dunged_level") != 0) {
65                 fprintf(stderr, "invalid or corrupted level file: %s\n", fname);
66                 ts_free_tree(ts);
67                 return -1;
68         }
69
70         if((sz = ts_get_attr_int(ts, "size", 0)) <= 0) {
71                 sz = 32;
72         }
73
74         if(init_level(lvl, sz, sz) == -1) {
75                 fprintf(stderr, "failed to initialize a %dx%d level\n", sz, sz);
76                 ts_free_tree(ts);
77                 return -1;
78         }
79
80         lvl->fname = strdup(fname);
81         if((lvl->dirname = malloc(strlen(fname) + 1))) {
82                 path_dir(lvl->fname, lvl->dirname);
83         }
84
85         lvl->cell_size = ts_get_attr_num(ts, "cellsize", DEF_CELL_SIZE);
86
87         if((vecptr = ts_get_attr_vec(ts, "player", 0))) {
88                 lvl->px = vecptr[0];
89                 lvl->py = vecptr[1];
90         }
91
92         if((str = ts_get_attr_str(ts, "tileset", 0))) {
93                 tset_path = alloca(strlen(str) + strlen(lvl->dirname) + 2);
94                 combine_path(lvl->dirname, str, tset_path);
95                 lvl->tset = get_tileset(tset_path);
96         }
97
98         iter = ts->child_list;
99         while(iter) {
100                 node = iter;
101                 iter = iter->next;
102
103                 if(strcmp(node->name, "cell") == 0) {
104                         cx = ts_get_attr_int(node, "x", -1);
105                         cy = ts_get_attr_int(node, "y", -1);
106                         if(cx < 0 || cy < 0 || cx >= sz || cy >= sz) {
107                                 fprintf(stderr, "ignoring cell with invalid or missing coordinates\n");
108                                 continue;
109                         }
110                         cell = lvl->cells + cy * sz + cx;
111                         cell->type = ts_get_attr_int(node, "blocked", 0) ? CELL_BLOCKED : CELL_WALK;
112
113                         /* abuse the next pointer to hang the treestore node temporarilly */
114                         cell->next = (struct cell*)node;
115                 }
116         }
117
118         cell = lvl->cells;
119         for(i=0; i<lvl->height; i++) {
120                 for(j=0; j<lvl->width; j++) {
121                         node = (struct ts_node*)cell->next;
122                         cell->next = 0;
123
124                         if(cell->type == CELL_SOLID) {
125                                 cell++;
126                                 continue;
127                         }
128
129                         if((tiletype = tile_type(ts_get_attr_str(node, "tiletype", 0))) == -1) {
130                                 /* no tile-type specified, try to guess */
131                                 tiletype = detect_cell_tile(lvl, j, i, &cell->tilerot);
132                         }
133
134                         if(lvl->tset) {
135                                 cell->tile = get_tile(lvl->tset, tiletype);
136                         }
137                         cell++;
138                 }
139         }
140
141         ts_free_tree(ts);
142         return 0;
143 }
144
145 /* TODO: save tileset info */
146 int save_level(struct level *lvl, const char *fname)
147 {
148         int i, j;
149         struct ts_node *root, *node;
150         struct ts_attr *attr;
151         struct cell *cell;
152
153         if(!(root = ts_alloc_node()) || ts_set_node_name(root, "dunged_level") == -1) {
154                 goto err;
155         }
156
157         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "size") == -1) {
158                 ts_free_attr(attr);
159                 goto err;
160         }
161         ts_set_valuei(&attr->val, lvl->width);
162         ts_add_attr(root, attr);
163
164         if(lvl->cell_size && (attr = ts_alloc_attr())) {
165                 ts_set_attr_name(attr, "cellsize");
166                 ts_set_valuef(&attr->val, lvl->cell_size);
167                 ts_add_attr(root, attr);
168         }
169
170         if(lvl->px >= 0 && lvl->px < lvl->width && lvl->py >= 0 && lvl->py < lvl->height) {
171                 if((attr = ts_alloc_attr())) {
172                         ts_set_attr_name(attr, "player");
173                         ts_set_valueiv(&attr->val, 2, lvl->px, lvl->py);
174                         ts_add_attr(root, attr);
175                 }
176         }
177
178         for(i=0; i<lvl->height; i++) {
179                 for(j=0; j<lvl->width; j++) {
180                         cell = lvl->cells + i * lvl->width + j;
181                         if(cell->type == CELL_SOLID) continue;
182
183                         if(!(node = ts_alloc_node()) || ts_set_node_name(node, "cell") == -1) {
184                                 ts_free_node(node);
185                                 goto err;
186                         }
187                         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "x") == -1) {
188                                 ts_free_attr(attr);
189                                 goto err;
190                         }
191                         ts_set_valuei(&attr->val, j);
192                         ts_add_attr(node, attr);
193                         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "y") == -1) {
194                                 ts_free_attr(attr);
195                                 goto err;
196                         }
197                         ts_set_valuei(&attr->val, i);
198                         ts_add_attr(node, attr);
199
200                         if(cell->type == CELL_BLOCKED) {
201                                 if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "blocked") == -1) {
202                                         ts_free_attr(attr);
203                                         goto err;
204                                 }
205                                 ts_set_valuei(&attr->val, 1);
206                                 ts_add_attr(node, attr);
207                         }
208
209                         ts_add_child(root, node);
210                 }
211         }
212
213         if(ts_save(root, fname) == -1) {
214                 fprintf(stderr, "failed to save level: %s\n", fname);
215                 ts_free_tree(root);
216                 return -1;
217         }
218         ts_free_tree(root);
219         return 0;
220
221 err:
222         fprintf(stderr, "failed to construct treestore tree\n");
223         ts_free_tree(root);
224         return -1;
225 }
226
227 #ifndef LEVEL_EDITOR
228
229 int get_cell_type(struct level *lvl, int x, int y)
230 {
231         if(x < 0 || x >= lvl->width || y < 0 || y >= lvl->height) {
232                 return CELL_SOLID;
233         }
234         return lvl->cells[y * lvl->width + x].type;
235 }
236
237 static int detect_cell_tile(struct level *lvl, int x, int y, int *rot)
238 {
239         int i, j, bit;
240         unsigned int adj = 0;
241
242         bit = 0;
243         for(i=0; i<3; i++) {
244                 for(j=0; j<3; j++) {
245                         if(get_cell_type(lvl, x + j - 1, y + i - 1) == CELL_SOLID) {
246                                 adj |= 1 << bit;
247                         }
248                         bit++;
249                 }
250         }
251
252         *rot = 0;
253
254         switch(adj) {
255         case 0:
256         case 0757:
257                 /* really we'd need a separate tile type for "all neighbors solid", but we
258                  * probably never going to need that in practice, so fuck it.
259                  */
260                 return TILE_OPEN;
261
262         case 0745:
263         case 0645:
264         case 0741:
265         case 0641:
266                 return TILE_CORNER;
267
268         case 0744:
269         case 0644:
270         case 0740:
271         case 0640:
272                 return TILE_OPENCORNER;
273
274         case 0715:
275         case 0315:
276         case 0714:
277         case 0314:
278                 *rot = 1;
279                 return TILE_CORNER;
280
281         case 0711:
282         case 0311:
283         case 0710:
284         case 0310:
285                 *rot = 1;
286                 return TILE_OPENCORNER;
287
288         case 0547:
289         case 0147:
290         case 0546:
291         case 0146:
292                 *rot = 3;
293                 return TILE_CORNER;
294
295         case 0447:
296         case 0047:
297         case 0446:
298         case 0046:
299                 *rot = 3;
300                 return TILE_OPENCORNER;
301
302         case 0517:
303         case 0417:
304         case 0513:
305         case 0413:
306                 *rot = 2;
307                 return TILE_CORNER;
308
309         case 0117:
310         case 0017:
311         case 0113:
312         case 0013:
313                 *rot = 2;
314                 return TILE_OPENCORNER;
315
316         case 0507:      /* N tee */
317                 *rot = 3;
318                 return TILE_TEE;
319         case 0515:      /* W tee */
320                 *rot = 2;
321                 return TILE_TEE;
322         case 0705:      /* S tee */
323                 *rot = 1;
324                 return TILE_TEE;
325         case 0545:      /* E tee */
326                 return TILE_TEE;
327
328         case 0505:      /* cross */
329                 return TILE_CROSS;
330
331         case 0404:      /* E str2open */
332                 return TILE_STR2OPEN;
333         case 0005:      /* N str2open */
334                 *rot = 1;
335                 return TILE_STR2OPEN;
336         case 0101:      /* W str2open */
337                 *rot = 2;
338                 return TILE_STR2OPEN;
339         case 0500:      /* S str2open */
340                 *rot = 3;
341                 return TILE_STR2OPEN;
342
343         default:
344                 if((adj & 0222) == 0200) {
345                         return TILE_STROPEN;
346                 }
347                 if((adj & 0222) == 0002) {
348                         *rot = 2;
349                         return TILE_STROPEN;
350                 }
351                 if((adj & 0070) == 0040) {
352                         *rot = 3;
353                         return TILE_STROPEN;
354                 }
355                 if((adj & 0070) == 0010) {
356                         *rot = 1;
357                         return TILE_STROPEN;
358                 }
359
360                 if((adj & 0070) == 0050) {
361                         *rot = 1;                       /* straight N-S */
362                         return TILE_STR;
363                 }
364                 if((adj & 0202) == 0202) {
365                         return TILE_STR;        /* straight E-W */
366                 }
367         }
368
369         return TILE_OPEN;
370 }
371
372 #endif  /* !LEVEL_EDITOR */