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