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