glasstower: smooth glass goal
[neverball] / ball / set.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <assert.h>
18 #include <errno.h>
19
20 #include "glext.h"
21 #include "config.h"
22 #include "image.h"
23 #include "text.h"
24 #include "set.h"
25 #include "game.h"
26 #include "common.h"
27
28 /*---------------------------------------------------------------------------*/
29
30 struct set
31 {
32     char file[PATHMAX];
33
34     char *id;                  /* Internal set identifier    */
35     char *name;                /* Set name                   */
36     char *desc;                /* Set description            */
37     char *shot;                /* Set screen-shot            */
38
39     char user_scores[PATHMAX]; /* User high-score file       */
40
41     struct score coin_score;   /* Challenge score            */
42     struct score time_score;   /* Challenge score            */
43
44     /* Level info */
45
46     int   count;                /* Number of levels           */
47     char *level_name_v[MAXLVL]; /* List of level file names   */
48 };
49
50 static int set_state = 0;
51
52 static int set;
53 static int count;
54
55 static struct set   set_v[MAXSET];
56 static struct level level_v[MAXLVL];
57
58 /*---------------------------------------------------------------------------*/
59
60 static void put_score(FILE *fp, const struct score *s)
61 {
62     int j;
63
64     for (j = 0; j < NSCORE; j++)
65         fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
66 }
67
68 void set_store_hs(void)
69 {
70     const struct set *s = &set_v[set];
71     FILE *fout;
72     int i;
73     const struct level *l;
74     char states[MAXLVL + 1];
75
76     if ((fout = fopen(config_user(s->user_scores), "w")))
77     {
78         for (i = 0; i < s->count; i++)
79         {
80             if (level_v[i].is_locked)
81                 states[i] = 'L';
82             else if (level_v[i].is_completed)
83                 states[i] = 'C';
84             else
85                 states[i] = 'O';
86         }
87         states[s->count] = '\0';
88         fprintf(fout, "%s\n",states);
89
90         put_score(fout, &s->time_score);
91         put_score(fout, &s->coin_score);
92
93         for (i = 0; i < s->count; i++)
94         {
95             l = &level_v[i];
96
97             put_score(fout, &l->score.best_times);
98             put_score(fout, &l->score.unlock_goal);
99             put_score(fout, &l->score.most_coins);
100         }
101
102         fclose(fout);
103     }
104 }
105
106 static int get_score(FILE *fp, struct score *s)
107 {
108     int j;
109     int res = 1;
110
111     for (j = 0; j < NSCORE && res; j++)
112     {
113         res = fscanf(fp, "%d %d %s\n",
114                      &s->timer[j],
115                      &s->coins[j],
116                      s->player[j]) == 3;
117     }
118     return res;
119 }
120
121 /* Get the score of the set. */
122 static void set_load_hs(void)
123 {
124     struct set *s = &set_v[set];
125     FILE *fin;
126     int i;
127     int res = 0;
128     struct level *l;
129     const char *fn = config_user(s->user_scores);
130     char states[MAXLVL + 1];
131
132     if ((fin = fopen(fn, "r")))
133     {
134         res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
135
136         for (i = 0; i < s->count && res; i++)
137         {
138             switch (states[i])
139             {
140             case 'L':
141                 level_v[i].is_locked = 1;
142                 level_v[i].is_completed = 0;
143                 break;
144
145             case 'C':
146                 level_v[i].is_locked = 0;
147                 level_v[i].is_completed = 1;
148                 break;
149
150             case 'O':
151                 level_v[i].is_locked = 0;
152                 level_v[i].is_completed = 0;
153                 break;
154
155             default:
156                 res = 0;
157             }
158         }
159
160         res = res &&
161             get_score(fin, &s->time_score) &&
162             get_score(fin, &s->coin_score);
163
164         for (i = 0; i < s->count && res; i++)
165         {
166             l = &level_v[i];
167             res = get_score(fin, &l->score.best_times) &&
168                 get_score(fin, &l->score.unlock_goal) &&
169                 get_score(fin, &l->score.most_coins);
170         }
171
172         fclose(fin);
173     }
174
175     if (!res && errno != ENOENT)
176     {
177         fprintf(stderr,
178                 L_("Error while loading user high-score file '%s': %s\n"),
179                 fn, errno ? strerror(errno) : L_("Incorrect format"));
180     }
181 }
182
183 /*---------------------------------------------------------------------------*/
184
185 static int set_load(struct set *s, const char *filename)
186 {
187     FILE *fin;
188     char *scores, *level_name;
189
190     fin = fopen(config_data(filename), "r");
191
192     if (!fin)
193     {
194         fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
195                 filename, strerror(errno));
196         return 0;
197     }
198
199     memset(s, 0, sizeof (struct set));
200
201     /* Set some sane values in case the scores are missing. */
202
203     score_init_hs(&s->time_score, 359999, 0);
204     score_init_hs(&s->coin_score, 359999, 0);
205
206     strncpy(s->file, filename, PATHMAX - 1);
207
208     if (read_line(&s->name, fin) &&
209         read_line(&s->desc, fin) &&
210         read_line(&s->id,   fin) &&
211         read_line(&s->shot, fin) &&
212         read_line(&scores,  fin))
213     {
214         sscanf(scores, "%d %d %d %d %d %d",
215                &s->time_score.timer[0],
216                &s->time_score.timer[1],
217                &s->time_score.timer[2],
218                &s->coin_score.coins[0],
219                &s->coin_score.coins[1],
220                &s->coin_score.coins[2]);
221
222         free(scores);
223
224         strncpy(s->user_scores, "neverballhs-", PATHMAX - 1);
225         strncat(s->user_scores, s->id, PATHMAX - 1 - strlen("neverballhs-"));
226
227         s->count = 0;
228
229         while (s->count < MAXLVL && read_line(&level_name, fin))
230         {
231             s->level_name_v[s->count] = level_name;
232             s->count++;
233         }
234
235         fclose(fin);
236
237         return 1;
238     }
239
240     free(s->name);
241     free(s->desc);
242     free(s->id);
243     free(s->shot);
244
245     fclose(fin);
246
247     return 0;
248 }
249
250 int set_init()
251 {
252     FILE *fin;
253     char *name;
254
255     if (set_state)
256         set_free();
257
258     set   = 0;
259     count = 0;
260
261     if ((fin = fopen(config_data(SET_FILE), "r")))
262     {
263         while (count < MAXSET && read_line(&name, fin))
264         {
265             if (set_load(&set_v[count], name))
266                 count++;
267
268             free(name);
269         }
270         fclose(fin);
271
272         set_state = 1;
273     }
274
275     return count;
276 }
277
278 void set_free(void)
279 {
280     int i, j;
281
282     for (i = 0; i < count; i++)
283     {
284         free(set_v[i].name);
285         free(set_v[i].desc);
286         free(set_v[i].id);
287         free(set_v[i].shot);
288
289         for (j = 0; j < set_v[i].count; j++)
290             free(set_v[i].level_name_v[j]);
291     }
292
293     set_state = 0;
294 }
295
296 /*---------------------------------------------------------------------------*/
297
298 int set_exists(int i)
299 {
300     return (0 <= i && i < count);
301 }
302
303 const char *set_name(int i)
304 {
305     return set_exists(i) ? _(set_v[i].name) : NULL;
306 }
307
308 const char *set_desc(int i)
309 {
310     return set_exists(i) ? _(set_v[i].desc) : NULL;
311 }
312
313 const char *set_shot(int i)
314 {
315     return set_exists(i) ? set_v[i].shot : NULL;
316 }
317
318 const struct score *set_time_score(int i)
319 {
320     return set_exists(i) ? &set_v[i].time_score : NULL;
321 }
322
323 const struct score *set_coin_score(int i)
324 {
325     return set_exists(i) ? &set_v[i].coin_score : NULL;
326 }
327
328 /*---------------------------------------------------------------------------*/
329
330 int set_level_exists(int s, int i)
331 {
332     return (i >= 0 && i < set_v[s].count);
333 }
334
335 static void set_load_levels(void)
336 {
337     struct level *l;
338     int nb = 1, bnb = 1;
339
340     int i;
341
342     const char *roman[] = {
343         "",
344         "I",   "II",   "III",   "IV",   "V",
345         "VI",  "VII",  "VIII",  "IX",   "X",
346         "XI",  "XII",  "XIII",  "XIV",  "XV",
347         "XVI", "XVII", "XVIII", "XIX",  "XX",
348         "XXI", "XXII", "XXIII", "XXIV", "XXV"
349     };
350
351     for (i = 0; i < set_v[set].count; i++)
352     {
353         l = &level_v[i];
354
355         level_load(set_v[set].level_name_v[i], l);
356
357         l->set    = &set_v[set];
358         l->number = i;
359
360         if (l->is_bonus)
361             sprintf(l->repr, "%s",   roman[bnb++]);
362         else
363             sprintf(l->repr, "%02d", nb++);
364
365         l->is_locked    = 1;
366         l->is_completed = 0;
367     }
368
369     /* Unlock first level. */
370
371     level_v[0].is_locked = 0;
372 }
373
374 void set_goto(int i)
375 {
376     set = i;
377
378     set_load_levels();
379     set_load_hs();
380 }
381
382 int curr_set(void)
383 {
384     return set;
385 }
386
387 struct level *get_level(int i)
388 {
389     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
390 }
391
392 /*---------------------------------------------------------------------------*/
393
394 int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
395 {
396     struct set *s = &set_v[set];
397     char player[MAXSTR] = "";
398
399     config_get_s(CONFIG_PLAYER, player, MAXSTR);
400
401     if (score_rank)
402         *score_rank = score_coin_insert(&s->coin_score, player, timer, coins);
403
404     if (times_rank)
405         *times_rank = score_time_insert(&s->time_score, player, timer, coins);
406
407     if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
408         return 1;
409     else
410         return 0;
411 }
412
413 void set_rename_player(int score_rank, int times_rank, const char *player)
414 {
415     struct set *s = &set_v[set];
416
417     strncpy(s->coin_score.player[score_rank], player, MAXNAM);
418     strncpy(s->time_score.player[times_rank], player, MAXNAM);
419 }
420
421 /*---------------------------------------------------------------------------*/
422
423 void level_snap(int i)
424 {
425     char filename[MAXSTR];
426     char *ext;
427
428     /* Convert the level name to a PNG filename. */
429
430     memset(filename, 0, MAXSTR);
431
432     ext = strrchr(level_v[i].file, '.');
433     strncpy(filename, level_v[i].file,
434             ext ? ext - level_v[i].file : strlen(level_v[i].file));
435     strcat(filename, ".png");
436
437     /* Initialize the game for a snapshot. */
438
439     if (game_init(level_v[i].file, 0, 1))
440     {
441         /* Render the level and grab the screen. */
442
443         config_clear();
444         game_set_fly(1.f);
445         game_kill_fade();
446         game_draw(1, 0);
447         SDL_GL_SwapBuffers();
448
449         image_snap(filename);
450     }
451 }
452
453 void set_cheat(void)
454 {
455     int i;
456
457     for (i = 0; i < set_v[set].count; i++)
458         level_v[i].is_locked = 0;
459 }
460
461 /*---------------------------------------------------------------------------*/