9e5fe44c633fcffa989af1eb6fdeb6cf64203b7f
[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 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                         /* TODO wall tiles and detail objects */
79                 }
80         }
81
82         ts_free_tree(ts);
83         return 0;
84 }
85
86 /* TODO: save tileset info */
87 int save_level(struct level *lvl, const char *fname)
88 {
89         int i, j;
90         struct ts_node *root, *node;
91         struct ts_attr *attr;
92         struct cell *cell;
93
94         if(!(root = ts_alloc_node()) || ts_set_node_name(root, "dunged_level") == -1) {
95                 goto err;
96         }
97
98         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "size") == -1) {
99                 ts_free_attr(attr);
100                 goto err;
101         }
102         ts_set_valuei(&attr->val, lvl->width);
103         ts_add_attr(root, attr);
104
105         for(i=0; i<lvl->height; i++) {
106                 for(j=0; j<lvl->width; j++) {
107                         cell = lvl->cells + i * lvl->width + j;
108                         if(cell->type == CELL_SOLID) continue;
109
110                         if(!(node = ts_alloc_node()) || ts_set_node_name(node, "cell") == -1) {
111                                 ts_free_node(node);
112                                 goto err;
113                         }
114                         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "x") == -1) {
115                                 ts_free_attr(attr);
116                                 goto err;
117                         }
118                         ts_set_valuei(&attr->val, j);
119                         ts_add_attr(node, attr);
120                         if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "y") == -1) {
121                                 ts_free_attr(attr);
122                                 goto err;
123                         }
124                         ts_set_valuei(&attr->val, i);
125                         ts_add_attr(node, attr);
126
127                         if(cell->type == CELL_BLOCKED) {
128                                 if(!(attr = ts_alloc_attr()) || ts_set_attr_name(attr, "blocked") == -1) {
129                                         ts_free_attr(attr);
130                                         goto err;
131                                 }
132                                 ts_set_valuei(&attr->val, 1);
133                                 ts_add_attr(node, attr);
134                         }
135
136                         ts_add_child(root, node);
137                 }
138         }
139
140         if(ts_save(root, fname) == -1) {
141                 fprintf(stderr, "failed to save level: %s\n", fname);
142                 ts_free_tree(root);
143                 return -1;
144         }
145         ts_free_tree(root);
146         return 0;
147
148 err:
149         fprintf(stderr, "failed to construct treestore tree\n");
150         ts_free_tree(root);
151         return -1;
152 }
153
154 static int load_tileset(struct level *lvl, struct ts_node *tsn)
155 {
156         static const char *tile_types[] = {"straight", "corner", "door", 0};
157
158         int i;
159         char *path;
160         const char *str;
161         struct ts_node *node;
162         struct tile *tile;
163
164         node = tsn->child_list;
165         while(node) {
166                 if(strcmp(node->name, "tile") == 0) {
167                         if(!(tile = calloc(1, sizeof *tile))) {
168                                 fprintf(stderr, "failed to allocate tile\n");
169                                 return -1;
170                         }
171                         if((str = ts_get_attr_str(node, "name", 0))) {
172                                 tile->name = strdup(str);
173                         }
174                         if((str = ts_get_attr_str(node, "type", 0))) {
175                                 for(i=0; tile_types[i]; i++) {
176                                         if(strcmp(str, tile_types[i]) == 0) {
177                                                 tile->type = i;
178                                                 break;
179                                         }
180                                 }
181                         }
182                         if((str = ts_get_attr_str(node, "scene", 0))) {
183                                 if(lvl->dirname) {
184                                         path = alloca(strlen(lvl->dirname) + strlen(str) + 2);
185                                         combine_path(lvl->dirname, str, path);
186                                 } else {
187                                         path = (char*)str;
188                                 }
189                                 load_scenefile(&tile->scn, path);
190                         }
191
192                         if(tile->name && tile->scn.meshlist) {  /* valid tile */
193                                 tile->next = lvl->tiles;
194                                 lvl->tiles = tile;
195                         } else {
196                                 fprintf(stderr, "load_tileset: skipping invalid tile: %s\n",
197                                                 tile->name ? tile->name : "missing tile name");
198                                 free(tile);
199                         }
200                 }
201                 node = node->next;
202         }
203
204         return 0;
205 }