6f265c38434fcd6d268e2b73912cb06c4b0af27c
[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 "fs.h"
8
9 static int load_tileset(struct level *lvl, struct ts_node *tsn);
10
11 int init_level(struct level *lvl, int xsz, int ysz)
12 {
13         if(!(lvl->cells = calloc(xsz * ysz, sizeof *lvl->cells))) {
14                 free(lvl);
15                 return -1;
16         }
17         lvl->width = xsz;
18         lvl->height = ysz;
19         lvl->cell_size = DEF_CELL_SIZE;
20         return 0;
21 }
22
23 void destroy_level(struct level *lvl)
24 {
25         if(!lvl) return;
26         free(lvl->cells);
27         free(lvl->fname);
28         free(lvl->dirname);
29 }
30
31 int load_level(struct level *lvl, const char *fname)
32 {
33         struct ts_node *ts, *node, *iter;
34         int i, j, sz, cx, cy;
35         struct cell *cell;
36         float *vecptr;
37
38         lvl->fname = strdup(fname);
39         if((lvl->dirname = malloc(strlen(fname) + 1))) {
40                 path_dir(lvl->fname, lvl->dirname);
41         }
42
43         if(!(ts = ts_load(fname))) {
44                 fprintf(stderr, "failed to load level: %s\n", fname);
45                 return -1;
46         }
47         if(strcmp(ts->name, "dunged_level") != 0) {
48                 fprintf(stderr, "invalid or corrupted level file: %s\n", fname);
49                 ts_free_tree(ts);
50                 return -1;
51         }
52
53         if((sz = ts_get_attr_int(ts, "size", 0)) <= 0) {
54                 sz = 32;
55         }
56
57         if(init_level(lvl, sz, sz) == -1) {
58                 fprintf(stderr, "failed to initialize a %dx%d level\n", sz, sz);
59                 ts_free_tree(ts);
60                 return -1;
61         }
62         lvl->cell_size = ts_get_attr_num(ts, "cellsize", DEF_CELL_SIZE);
63
64         if((vecptr = ts_get_attr_vec(ts, "player", 0))) {
65                 lvl->px = vecptr[0];
66                 lvl->py = vecptr[1];
67         }
68
69         iter = ts->child_list;
70         while(iter) {
71                 node = iter;
72                 iter = iter->next;
73
74                 if(strcmp(node->name, "tileset") == 0) {
75                         load_tileset(lvl, node);
76
77                 } else if(strcmp(node->name, "cell") == 0) {
78                         cx = ts_get_attr_int(node, "x", -1);
79                         cy = ts_get_attr_int(node, "y", -1);
80                         if(cx < 0 || cy < 0 || cx >= sz || cy >= sz) {
81                                 fprintf(stderr, "ignoring cell with invalid or missing coordinates\n");
82                                 continue;
83                         }
84                         cell = lvl->cells + cy * sz + cx;
85                         cell->type = ts_get_attr_int(node, "blocked", 0) ? CELL_BLOCKED : CELL_WALK;
86
87                         /* abuse the next pointer to hang the treestore node temporarilly */
88                         cell->next = (struct cell*)node;
89                 }
90         }
91
92         /* assign wall types to all occupied cells */
93         cell = lvl->cells;
94         for(i=0; i<lvl->height; i++) {
95                 for(j=0; j<lvl->width; j++) {
96                         if(cell->type == CELL_SOLID) {
97                                 cell++;
98                                 continue;
99                         }
100
101                         /* TODO take wall choice from the level file into account */
102                         /* TODO detect corners */
103                         node = (struct ts_node*)cell->next;
104                         cell->next = 0;
105
106                         if(j <= 0 || cell[-1].type == CELL_SOLID) {
107                                 cell->wall[0] = TILE_STRAIGHT;
108                         }
109                         if(i <= 0 || cell[-lvl->width].type == CELL_SOLID) {
110                                 cell->wall[1] = TILE_STRAIGHT;
111                         }
112                         if(j >= lvl->width - 1 || cell[1].type == CELL_SOLID) {
113                                 cell->wall[2] = TILE_STRAIGHT;
114                         }
115                         if(i >= lvl->height - 1 || cell[lvl->width].type == CELL_SOLID) {
116                                 cell->wall[3] = TILE_STRAIGHT;
117                         }
118
119                         cell++;
120                 }
121         }
122
123         ts_free_tree(ts);
124         return 0;
125 }
126
127 /* TODO: save tileset info */
128 int save_level(struct level *lvl, const char *fname)
129 {
130         int i, j;
131         struct ts_node *root, *node;
132         struct ts_attr *attr;
133         struct cell *cell;
134
135         if(!(root = ts_alloc_node()) || ts_set_node_name(root, "dunged_level") == -1) {
136                 goto err;
137         }
138
139         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "size") == -1) {
140                 ts_free_attr(attr);
141                 goto err;
142         }
143         ts_set_valuei(&attr->val, lvl->width);
144         ts_add_attr(root, attr);
145
146         for(i=0; i<lvl->height; i++) {
147                 for(j=0; j<lvl->width; j++) {
148                         cell = lvl->cells + i * lvl->width + j;
149                         if(cell->type == CELL_SOLID) continue;
150
151                         if(!(node = ts_alloc_node()) || ts_set_node_name(node, "cell") == -1) {
152                                 ts_free_node(node);
153                                 goto err;
154                         }
155                         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "x") == -1) {
156                                 ts_free_attr(attr);
157                                 goto err;
158                         }
159                         ts_set_valuei(&attr->val, j);
160                         ts_add_attr(node, attr);
161                         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "y") == -1) {
162                                 ts_free_attr(attr);
163                                 goto err;
164                         }
165                         ts_set_valuei(&attr->val, i);
166                         ts_add_attr(node, attr);
167
168                         if(cell->type == CELL_BLOCKED) {
169                                 if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "blocked") == -1) {
170                                         ts_free_attr(attr);
171                                         goto err;
172                                 }
173                                 ts_set_valuei(&attr->val, 1);
174                                 ts_add_attr(node, attr);
175                         }
176
177                         ts_add_child(root, node);
178                 }
179         }
180
181         if(ts_save(root, fname) == -1) {
182                 fprintf(stderr, "failed to save level: %s\n", fname);
183                 ts_free_tree(root);
184                 return -1;
185         }
186         ts_free_tree(root);
187         return 0;
188
189 err:
190         fprintf(stderr, "failed to construct treestore tree\n");
191         ts_free_tree(root);
192         return -1;
193 }
194
195 static int load_tileset(struct level *lvl, struct ts_node *tsn)
196 {
197         static const char *tile_types[] = {"empty", "straight", "corner", "door", 0};
198
199         int i;
200         char *path;
201         const char *str;
202         struct ts_node *node;
203         struct tile *tile;
204
205         node = tsn->child_list;
206         while(node) {
207                 if(strcmp(node->name, "tile") == 0) {
208                         if(!(tile = calloc(1, sizeof *tile))) {
209                                 fprintf(stderr, "failed to allocate tile\n");
210                                 return -1;
211                         }
212                         if((str = ts_get_attr_str(node, "name", 0))) {
213                                 tile->name = strdup(str);
214                         }
215                         if((str = ts_get_attr_str(node, "type", 0))) {
216                                 for(i=0; tile_types[i]; i++) {
217                                         if(strcmp(str, tile_types[i]) == 0) {
218                                                 tile->type = i;
219                                                 break;
220                                         }
221                                 }
222                         }
223                         if((str = ts_get_attr_str(node, "scene", 0))) {
224                                 if(lvl->dirname) {
225                                         path = alloca(strlen(lvl->dirname) + strlen(str) + 2);
226                                         combine_path(lvl->dirname, str, path);
227                                 } else {
228                                         path = (char*)str;
229                                 }
230                                 load_scenefile(&tile->scn, path);
231                         }
232
233                         if(tile->name && tile->scn.meshlist) {  /* valid tile */
234                                 tile->next = lvl->tiles;
235                                 lvl->tiles = tile;
236                         } else {
237                                 fprintf(stderr, "load_tileset: skipping invalid tile: %s\n",
238                                                 tile->name ? tile->name : "missing tile name");
239                                 free(tile);
240                         }
241                 }
242                 node = node->next;
243         }
244
245         return 0;
246 }
247
248 struct tile *find_level_tile(struct level *lvl, int type)
249 {
250         struct tile *tile = lvl->tiles;
251         while(tile) {
252                 if(tile->type == type) {
253                         return tile;
254                 }
255                 tile = tile->next;
256         }
257         return 0;
258 }
259
260 int gen_cell_geom(struct level *lvl, struct cell *cell)
261 {
262         int i;
263         struct meshgroup *wallgeom;
264         struct tile *tstr;
265         struct mesh *mesh, *tmesh;
266         float xform[16];
267
268         if(!(tstr = find_level_tile(lvl, TILE_STRAIGHT))) {
269                 return -1;
270         }
271
272         if(!(wallgeom = malloc(sizeof *wallgeom))) {
273                 return -1;
274         }
275         init_meshgroup(wallgeom);
276
277         for(i=0; i<4; i++) {
278                 if(cell->wall[i] == TILE_STRAIGHT) {    /* TODO: support other wall types */
279                         cgm_mrotation_y(xform, i * M_PI / 2.0f);
280
281                         tmesh = tstr->scn.meshlist;
282                         while(tmesh) {
283                                 if(!(mesh = malloc(sizeof *mesh))) {
284                                         return -1;
285                                 }
286
287                                 /* create a copy of the tile mesh */
288                                 if(copy_mesh(mesh, tmesh) == -1) {
289                                         free(mesh);
290                                         return -1;
291                                 }
292                                 if(i) xform_mesh(mesh, xform);  /* rotate it to match the wall angle */
293
294                                 /* add it to the level meshlist */
295                                 mesh->next = lvl->meshlist;
296                                 lvl->meshlist = mesh;
297
298                                 /* add it to the meshgroup */
299                                 if(add_meshgroup_mesh(wallgeom, mesh) == -1) {
300                                         destroy_mesh(mesh);
301                                         free(mesh);
302                                         return -1;
303                                 }
304
305                                 tmesh = tmesh->next;
306                         }
307                 }
308         }
309
310         /* TODO: append to other existing meshgroups for detail objects */
311         cell->mgrp = wallgeom;
312         cell->num_mgrp = 1;
313
314         return 0;
315 }
316
317 int gen_level_geom(struct level *lvl)
318 {
319         int i, j;
320         struct cell *cell;
321
322         for(i=0; i<lvl->height; i++) {
323                 for(j=0; j<lvl->width; j++) {
324                         cell = lvl->cells + i * lvl->width + j;
325                         if(cell->type != CELL_SOLID) {
326                                 if(gen_cell_geom(lvl, cell) == -1) {
327                                         printf("failed to generate cell\n");
328                                         return -1;
329                                 }
330                         }
331                 }
332         }
333         return 0;
334 }