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