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