new collision detection. cleanups. bug fixes. music approach patch from Ricardo.
[supertux.git] / src / level.c
1 //
2 // C Implementation: level
3 //
4 // Description:
5 //
6 //
7 // Author: Tobias Glaesser <tobi.web@gmx.de>, (C) 2003
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include "globals.h"
17 #include "setup.h"
18 #include "screen.h"
19 #include "level.h"
20 #include "physic.h"
21
22 texture_type img_bkgd, img_bkgd_tile[2][4], img_solid[4], img_brick[2];
23
24 void subset_init(st_subset* st_subset)
25 {
26   st_subset->title = NULL;
27   st_subset->description = NULL;
28   st_subset->name = NULL;
29   st_subset->levels = 0;
30 }
31
32 void subset_load(st_subset* st_subset, char *subset)
33 {
34   FILE* fi;
35   char filename[1024];
36   char str[1024];
37   int len,i;
38
39   st_subset->name = (char*) malloc(sizeof(char)*(strlen(subset)+1));
40   strcpy(st_subset->name,subset);
41
42   snprintf(filename, 1024, "%s/levels/%s/info", st_dir, subset);
43   if(!faccessible(filename))
44     snprintf(filename, 1024, "%s/levels/%s/info", DATA_PREFIX, subset);
45   if(faccessible(filename))
46     {
47       fi = fopen(filename, "r");
48       if (fi == NULL)
49         {
50           perror(filename);
51         }
52
53       /* Load title info: */
54       fgets(str, 40, fi);
55       st_subset->title = (char*) malloc(sizeof(char)*(strlen(str)+1));
56       strcpy(st_subset->title, str);
57
58       /* Load the description: */
59
60       str[0] = '\0';
61       st_subset->description = NULL;
62       len = 0;
63       while(fgets(str, 300, fi) != NULL)
64         {
65           len += strlen(str);
66           if(st_subset->description == NULL)
67             st_subset->description = (char*) calloc(len+1,sizeof(char));
68           else
69             st_subset->description = (char*) realloc(st_subset->description, sizeof(char) * (len+1));
70           strcat(st_subset->description,str);
71         }
72       fclose(fi);
73
74       snprintf(str, 1024, "%s.png", filename);
75       if(faccessible(str))
76         {
77           texture_load(&st_subset->image,str,IGNORE_ALPHA);
78         }
79       else
80         {
81           snprintf(filename, 1024, "%s/images/status/level-subset-info.png", DATA_PREFIX);
82           texture_load(&st_subset->image,filename,IGNORE_ALPHA);
83         }
84     }
85
86   for(i=1; i != -1; ++i)
87     {
88       /* Get the number of levels in this subset */
89       snprintf(filename, 1024, "%s/levels/%s/level%d.dat", st_dir, subset,i);
90       if(!faccessible(filename))
91         {
92           snprintf(filename, 1024, "%s/levels/%s/level%d.dat", DATA_PREFIX, subset,i);
93           if(!faccessible(filename))
94             break;
95         }
96     }
97   st_subset->levels = --i;
98 }
99
100 void subset_save(st_subset* st_subset)
101 {
102   FILE* fi;
103   char filename[1024];
104
105   /* Save data file: */
106   sprintf(filename, "/levels/%s/", st_subset->name);
107   
108   fcreatedir(filename);
109   snprintf(filename, 1024, "%s/levels/%s/info", st_dir, st_subset->name);
110   if(!fwriteable(filename))
111     snprintf(filename, 1024, "%s/levels/%s/info", DATA_PREFIX, st_subset->name);
112   if(fwriteable(filename))
113     {
114       fi = fopen(filename, "w");
115       if (fi == NULL)
116         {
117           perror(filename);
118         }
119
120       /* Save title info: */
121       fputs(st_subset->title, fi);
122       fputs("\n", fi);
123
124       /* Save the description: */
125
126       fputs(st_subset->description, fi);
127       fputs("\n", fi);
128       fclose(fi);
129
130     }
131 }
132
133 void subset_free(st_subset* st_subset)
134 {
135   free(st_subset->title);
136   free(st_subset->description);
137   free(st_subset->name);
138   texture_free(&st_subset->image);
139   st_subset->levels = 0;
140 }
141
142 /* Load data for this level: */
143 /* Returns -1, if the loading of the level failed. */
144 int level_load(st_level* plevel, char *subset, int level)
145 {
146   int y;
147   FILE * fi;
148   char str[80];
149   char filename[1024];
150   char * line;
151
152   /* Load data file: */
153
154   snprintf(filename, 1024, "%s/levels/%s/level%d.dat", st_dir, subset, level);
155   if(!faccessible(filename))
156     snprintf(filename, 1024, "%s/levels/%s/level%d.dat", DATA_PREFIX, subset, level);
157   fi = fopen(filename, "r");
158   if (fi == NULL)
159     {
160       perror(filename);
161       return -1;
162     }
163
164
165   /* Load header info: */
166
167
168   /* (Level title) */
169   fgets(str, 20, fi);
170   strcpy(plevel->name, str);
171   plevel->name[strlen(plevel->name)-1] = '\0';
172
173   /* (Level theme) */
174   fgets(str, 20, fi);
175   strcpy(plevel->theme, str);
176   plevel->theme[strlen(plevel->theme)-1] = '\0';
177
178
179
180   /* (Time to beat level) */
181   fgets(str, 10, fi);
182   plevel->time_left = atoi(str);
183
184   /* (Song file for this level) */
185   fgets(str, sizeof(plevel->song_title), fi);
186   strcpy(plevel->song_title, str);
187   plevel->song_title[strlen(plevel->song_title)-1] = '\0';
188
189   /* (Level background image) */
190   fgets(str, sizeof(plevel->bkgd_image), fi);
191   strcpy(plevel->bkgd_image, str);
192   plevel->bkgd_image[strlen(plevel->bkgd_image)-1] = '\0';
193
194   /* (Level background color) */
195   fgets(str, 10, fi);
196   plevel->bkgd_red = atoi(str);
197   fgets(str, 10, fi);
198   plevel->bkgd_green= atoi(str);
199   fgets(str, 10, fi);
200   plevel->bkgd_blue = atoi(str);
201
202   /* (Level width) */
203   fgets(str, 10, fi);
204   plevel->width = atoi(str);
205
206   /* (Level gravity) */
207   fgets(str, 10, fi);
208   plevel->gravity = atof(str);
209
210   /* Set the global gravity to the latest loaded level's gravity */
211   gravity = plevel->gravity;
212
213   /* Allocate some space for the line-reading! */
214
215   line = (char *) malloc(sizeof(char) * (plevel->width + 5));
216   if (line == NULL)
217     {
218       fprintf(stderr, "Couldn't allocate space to load level data!");
219       fclose(fi);
220       return -1;
221     }
222
223
224   /* Load the level lines: */
225
226   for (y = 0; y < 15; y++)
227     {
228       if(fgets(line, plevel->width + 5, fi) == NULL)
229         {
230           fprintf(stderr, "Level %s isn't complete!\n",plevel->name);
231           free(line);
232           fclose(fi);
233           return -1;
234         }
235       line[strlen(line) - 1] = '\0';
236       plevel->tiles[y] = (unsigned char*) strdup(line);
237     }
238
239   free(line);
240   fclose(fi);
241   return 0;
242 }
243
244 /* Save data for level: */
245
246 void level_save(st_level* plevel, char * subset, int level)
247 {
248   FILE * fi;
249   char filename[1024];
250   int y;
251   char str[80];
252
253   /* Save data file: */
254   sprintf(str, "/levels/%s/", subset);
255   fcreatedir(str);
256   snprintf(filename, 1024, "%s/levels/%s/level%d.dat", st_dir, subset, level);
257   if(!fwriteable(filename))
258     snprintf(filename, 1024, "%s/levels/%s/level%d.dat", DATA_PREFIX, subset, level);
259
260   fi = fopen(filename, "w");
261   if (fi == NULL)
262     {
263       perror(filename);
264       st_shutdown();
265       exit(-1);
266     }
267
268   fputs(plevel->name, fi);
269   fputs("\n", fi);
270   fputs(plevel->theme, fi);
271   fputs("\n", fi);
272   sprintf(str, "%d\n", plevel->time_left);      /* time */
273   fputs(str, fi);
274   fputs(plevel->song_title, fi);        /* song filename */
275   fputs("\n",fi);
276   fputs(plevel->bkgd_image, fi);        /* background image */
277   sprintf(str, "\n%d\n", plevel->bkgd_red);     /* red background color */
278   fputs(str, fi);
279   sprintf(str, "%d\n", plevel->bkgd_green);     /* green background color */
280   fputs(str, fi);
281   sprintf(str, "%d\n", plevel->bkgd_blue);      /* blue background color */
282   fputs(str, fi);
283   sprintf(str, "%d\n", plevel->width);  /* level width */
284   fputs(str, fi);
285   sprintf(str, "%2.1f\n", plevel->gravity);     /* level gravity */
286   fputs(str, fi);
287
288   for(y = 0; y < 15; ++y)
289     {
290       fputs((const char*)plevel->tiles[y], fi);
291       fputs("\n", fi);
292     }
293
294   fclose(fi);
295 }
296
297
298 /* Unload data for this level: */
299
300 void level_free(st_level* plevel)
301 {
302   int i;
303   for(i=0; i < 15; ++i)
304     free(plevel->tiles[i]);
305
306   plevel->name[0] = '\0';
307   plevel->theme[0] = '\0';
308   plevel->song_title[0] = '\0';
309   plevel->bkgd_image[0] = '\0';
310 }
311
312 /* Load graphics: */
313
314 void level_load_gfx(st_level *plevel)
315 {
316   level_load_image(&img_brick[0],plevel->theme,"brick0.png", IGNORE_ALPHA);
317   level_load_image(&img_brick[1],plevel->theme,"brick1.png", IGNORE_ALPHA);
318
319   level_load_image(&img_solid[0],plevel->theme,"solid0.png", USE_ALPHA);
320   level_load_image(&img_solid[1],plevel->theme,"solid1.png", USE_ALPHA);
321   level_load_image(&img_solid[2],plevel->theme,"solid2.png", USE_ALPHA);
322   level_load_image(&img_solid[3],plevel->theme,"solid3.png", USE_ALPHA);
323
324   level_load_image(&img_bkgd_tile[0][0],plevel->theme,"bkgd-00.png", USE_ALPHA);
325   level_load_image(&img_bkgd_tile[0][1],plevel->theme,"bkgd-01.png", USE_ALPHA);
326   level_load_image(&img_bkgd_tile[0][2],plevel->theme,"bkgd-02.png", USE_ALPHA);
327   level_load_image(&img_bkgd_tile[0][3],plevel->theme,"bkgd-03.png", USE_ALPHA);
328
329   level_load_image(&img_bkgd_tile[1][0],plevel->theme,"bkgd-10.png", USE_ALPHA);
330   level_load_image(&img_bkgd_tile[1][1],plevel->theme,"bkgd-11.png", USE_ALPHA);
331   level_load_image(&img_bkgd_tile[1][2],plevel->theme,"bkgd-12.png", USE_ALPHA);
332   level_load_image(&img_bkgd_tile[1][3],plevel->theme,"bkgd-13.png", USE_ALPHA);
333
334   if(strcmp(plevel->bkgd_image,"") != 0)
335     {
336       char fname[1024];
337       snprintf(fname, 1024, "%s/background/%s", st_dir, plevel->bkgd_image);
338       if(!faccessible(fname))
339         snprintf(fname, 1024, "%s/images/background/%s", DATA_PREFIX, plevel->bkgd_image);
340       texture_load(&img_bkgd, fname, IGNORE_ALPHA);
341     }
342 }
343
344 /* Free graphics data for this level: */
345
346 void level_free_gfx(void)
347 {
348   int i;
349
350   for (i = 0; i < 2; i++)
351     {
352       texture_free(&img_brick[i]);
353     }
354   for (i = 0; i < 4; i++)
355     {
356       texture_free(&img_solid[i]);
357       texture_free(&img_bkgd_tile[0][i]);
358       texture_free(&img_bkgd_tile[1][i]);
359     }
360   texture_free(&img_bkgd);
361 }
362
363 /* Load a level-specific graphic... */
364
365 void level_load_image(texture_type* ptexture, char* theme, char * file, int use_alpha)
366 {
367   char fname[1024];
368   
369   snprintf(fname, 1024, "%s/themes/%s/%s", st_dir, theme, file);
370   if(!faccessible(fname))
371     snprintf(fname, 1024, "%s/images/themes/%s/%s", DATA_PREFIX, theme, file);
372     
373   texture_load(ptexture, fname, use_alpha);
374 }
375
376 /* Edit a piece of the map! */
377
378 void level_change(st_level* plevel, float x, float y, unsigned char c)
379 {
380   int xx, yy;
381
382   yy = ((int)y / 32);
383   xx = ((int)x / 32);
384
385   if (yy >= 0 && yy < 15 && xx >= 0 && xx <= plevel->width)
386     plevel->tiles[yy][xx] = c;
387 }
388
389 /* Free music data for this level: */
390
391 void level_free_song(void)
392 {
393   free_music(level_song);
394   free_music(level_song_fast);
395 }
396
397 /* Load music: */
398
399 void level_load_song(st_level* plevel)
400 {
401
402   char * song_path;
403   char * song_subtitle;
404
405   song_path = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) +
406                               strlen(plevel->song_title) + 8));
407   sprintf(song_path, "%s/music/%s", DATA_PREFIX, plevel->song_title);
408   level_song = load_song(song_path);
409   free(song_path);
410
411
412   song_path = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) +
413                               strlen(plevel->song_title) + 8 + 5));
414   song_subtitle = strdup(plevel->song_title);
415   strcpy(strstr(song_subtitle, "."), "\0");
416   sprintf(song_path, "%s/music/%s-fast%s", DATA_PREFIX, song_subtitle, strstr(plevel->song_title, "."));
417   level_song_fast = load_song(song_path);
418   free(song_subtitle);
419   free(song_path);
420 }