starts to sortof look like a dungeon if you squint
[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[] = {"empty", "straight", "corner", "door", 0};
220
221         int i;
222         char *path;
223         const char *str;
224         struct ts_node *node;
225         struct tile *tile;
226
227         node = tsn->child_list;
228         while(node) {
229                 if(strcmp(node->name, "tile") == 0) {
230                         if(!(tile = calloc(1, sizeof *tile))) {
231                                 fprintf(stderr, "failed to allocate tile\n");
232                                 return -1;
233                         }
234                         if((str = ts_get_attr_str(node, "name", 0))) {
235                                 tile->name = strdup(str);
236                         }
237                         if((str = ts_get_attr_str(node, "type", 0))) {
238                                 for(i=0; tile_types[i]; i++) {
239                                         if(strcmp(str, tile_types[i]) == 0) {
240                                                 tile->type = i;
241                                                 break;
242                                         }
243                                 }
244                         }
245                         if((str = ts_get_attr_str(node, "scene", 0))) {
246                                 if(lvl->dirname) {
247                                         path = alloca(strlen(lvl->dirname) + strlen(str) + 2);
248                                         combine_path(lvl->dirname, str, path);
249                                 } else {
250                                         path = (char*)str;
251                                 }
252                                 load_scenefile(&tile->scn, path);
253                         }
254
255                         if(tile->name && tile->scn.meshlist) {  /* valid tile */
256                                 tile->next = lvl->tiles;
257                                 lvl->tiles = tile;
258                         } else {
259                                 fprintf(stderr, "load_tileset: skipping invalid tile: %s\n",
260                                                 tile->name ? tile->name : "missing tile name");
261                                 free(tile);
262                         }
263                 }
264                 node = node->next;
265         }
266
267         return 0;
268 }
269
270 struct tile *find_level_tile(struct level *lvl, int type)
271 {
272         struct tile *tile = lvl->tiles;
273         while(tile) {
274                 if(tile->type == type) {
275                         return tile;
276                 }
277                 tile = tile->next;
278         }
279         return 0;
280 }
281
282 int gen_cell_geom(struct level *lvl, struct cell *cell)
283 {
284         int i;
285         struct meshgroup *wallgeom;
286         struct tile *tstr;
287         struct mesh *mesh, *tmesh;
288         float xform[16];
289
290         if(!(tstr = find_level_tile(lvl, TILE_STRAIGHT))) {
291                 return -1;
292         }
293
294         if(!(wallgeom = malloc(sizeof *wallgeom))) {
295                 return -1;
296         }
297         init_meshgroup(wallgeom);
298
299         for(i=0; i<4; i++) {
300                 if(cell->wall[i] == TILE_STRAIGHT) {    /* TODO: support other wall types */
301                         cgm_mrotation_y(xform, i * M_PI / 2.0f);
302
303                         tmesh = tstr->scn.meshlist;
304                         while(tmesh) {
305                                 if(!(mesh = malloc(sizeof *mesh))) {
306                                         return -1;
307                                 }
308
309                                 /* create a copy of the tile mesh */
310                                 if(copy_mesh(mesh, tmesh) == -1) {
311                                         free(mesh);
312                                         return -1;
313                                 }
314                                 if(i) xform_mesh(mesh, xform);  /* rotate it to match the wall angle */
315
316                                 /* add it to the level meshlist */
317                                 mesh->next = lvl->meshlist;
318                                 lvl->meshlist = mesh;
319
320                                 /* add it to the meshgroup */
321                                 if(add_meshgroup_mesh(wallgeom, mesh) == -1) {
322                                         destroy_mesh(mesh);
323                                         free(mesh);
324                                         return -1;
325                                 }
326
327                                 tmesh = tmesh->next;
328                         }
329                 }
330         }
331
332         /* TODO: append to other existing meshgroups for detail objects */
333         cell->mgrp = wallgeom;
334         cell->num_mgrp = 1;
335
336         return 0;
337 }
338
339 int gen_level_geom(struct level *lvl)
340 {
341         int i, j;
342         struct cell *cell;
343
344         for(i=0; i<lvl->height; i++) {
345                 for(j=0; j<lvl->width; j++) {
346                         cell = lvl->cells + i * lvl->width + j;
347                         if(cell->type != CELL_SOLID) {
348                                 if(gen_cell_geom(lvl, cell) == -1) {
349                                         printf("failed to generate cell\n");
350                                         return -1;
351                                 }
352                         }
353                 }
354         }
355         return 0;
356 }
357
358 #else
359
360 static int load_tileset(struct level *lvl, struct ts_node *tsn)
361 {
362         return 0;               /* in the level editor we don't need tileset loading */
363 }
364
365 #endif  /* !LEVEL_EDITOR */