The proper bonus level is now included in the congratulatory message when a bonus...
[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
27 /*---------------------------------------------------------------------------*/
28
29 static int set;
30 static int count;
31
32 static struct set set_v[MAXSET];
33 static struct level level_v[MAXLVL];
34
35 /*---------------------------------------------------------------------------*/
36
37 static void put_score(FILE *fp, const struct score *s)
38 {
39     int j;
40
41     for (j = 0; j < NSCORE; j++)
42        fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
43 }
44
45 /* Store the score of the set. */
46 static void set_store_hs(void)
47 {
48     const struct set *s = &set_v[set];
49     FILE *fout;
50     int i;
51     const struct level *l;
52     char states[MAXLVL + 1];
53
54     if ((fout = fopen(config_user(s->user_scores), "w")))
55     {
56         for (i = 0; i < s->count; i++)
57         {
58             if (level_v[i].is_locked)
59                 states[i] = 'L';
60             else if (level_v[i].is_completed)
61                 states[i] = 'C';
62             else
63                 states[i] = 'O';
64         }
65         states[s->count] = '\0';
66         fprintf(fout, "%s\n",states);
67
68         put_score(fout, &s->time_score);
69         put_score(fout, &s->coin_score);
70
71         for (i = 0; i < s->count; i++)
72         {
73             l = &level_v[i];
74
75             put_score(fout, &l->score.best_times);
76             put_score(fout, &l->score.unlock_goal);
77             put_score(fout, &l->score.most_coins);
78         }
79
80         fclose(fout);
81     }
82 }
83
84 static int get_score(FILE *fp, struct score *s)
85 {
86     int j;
87     int res = 1;
88
89     for (j = 0; j < NSCORE && res; j++)
90     {
91         res = fscanf(fp, "%d %d %s\n",
92                      &s->timer[j],
93                      &s->coins[j],
94                      s->player[j]) == 3;
95     }
96     return res;
97 }
98
99 /* Get the score of the set. */
100 static void set_load_hs(void)
101 {
102     struct set *s = &set_v[set];
103     FILE *fin;
104     int i;
105     int res = 0;
106     struct level *l;
107     const char *fn = config_user(s->user_scores);
108     char states[MAXLVL + 1];
109
110     if ((fin = fopen(fn, "r")))
111     {
112         res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
113
114         for (i = 0; i < s->count && res; i++)
115         {
116             switch (states[i])
117             {
118             case 'L':
119                 level_v[i].is_locked = 1;
120                 level_v[i].is_completed = 0;
121                 break;
122
123             case 'C':
124                 level_v[i].is_locked = 0;
125                 level_v[i].is_completed = 1;
126                 break;
127
128             case 'O':
129                 level_v[i].is_locked = 0;
130                 level_v[i].is_completed = 0;
131                 break;
132
133             default:
134                 res = 0;
135             }
136         }
137
138         res = res &&
139             get_score(fin, &s->time_score) &&
140             get_score(fin, &s->coin_score);
141
142         for (i = 0; i < s->count && res; i++)
143         {
144             l = &level_v[i];
145             res = get_score(fin, &l->score.best_times) &&
146                   get_score(fin, &l->score.unlock_goal) &&
147                   get_score(fin, &l->score.most_coins);
148         }
149
150         fclose(fin);
151     }
152
153     if (!res && errno != ENOENT)
154     {
155         fprintf(stderr,
156                 L_("Error while loading user high-score file '%s': %s\n"),
157                 fn, errno ? strerror(errno) : L_("Incorrect format"));
158     }
159 }
160
161 static char *strip_eol(char *str)
162 {
163     char *c = str + strlen(str) - 1;
164
165     while (c >= str && (*c == '\n' || *c =='\r'))
166         *c-- = '\0';
167
168     return str;
169 }
170
171 static int set_load(struct set *s, const char *filename)
172 {
173     FILE *fin;
174     char buf[MAXSTR];
175     int res;
176
177     fin = fopen(config_data(filename), "r");
178
179     if (!fin)
180     {
181         fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
182                 filename, strerror(errno));
183         return 0;
184     }
185
186     memset(s, 0, sizeof (struct set));
187
188     /* Set some sane values in case the scores hs is missing. */
189
190     score_init_hs(&s->time_score, 359999, 0);
191     score_init_hs(&s->coin_score, 359999, 0);
192
193     /* Load set metadata. */
194
195     strcpy(s->file, filename);
196
197     if ((res = fgets(buf, MAXSTR, fin) != NULL))
198         strcpy(s->name, strip_eol(buf));
199     if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
200         strcpy(s->desc, strip_eol(buf));
201     if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
202         strcpy(s->id, strip_eol(buf));
203     if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
204         strcpy(s->shot, strip_eol(buf));
205     if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
206         sscanf(buf, "%d %d %d %d %d %d",
207                 &s->time_score.timer[0],
208                 &s->time_score.timer[1],
209                 &s->time_score.timer[2],
210                 &s->coin_score.coins[0],
211                 &s->coin_score.coins[1],
212                 &s->coin_score.coins[2]);
213
214     strcpy(s->user_scores, "neverballhs-");
215     strcat(s->user_scores, s->id);
216
217     /* Count levels. */
218
219     s->count = 0;
220
221     while (s->count < MAXLVL && (fscanf(fin, "%s", buf) == 1))
222         s->count++;
223
224     fclose(fin);
225
226     /* Load the levels states (stored in the user high score file) */
227
228     s->locked = s->count;
229     s->completed = 0;
230
231     if ((fin = fopen(config_user(s->user_scores), "r")))
232     {
233         char states[MAXLVL + 1];
234         int i;
235         if ((fscanf(fin, "%s\n", states) == 1) && (strlen(states) == s->count))
236         {
237             for (i = 0; i < s->count; i++)
238             {
239                 if (states[i] == 'O')
240                     s->locked -= 1;
241                 else if (states[i] == 'C')
242                 {
243                     s->completed += 1;
244                     s->locked -= 1;
245                 }
246             }
247         }
248         fclose(fin);
249     }
250     if (s->locked == s->count)
251         s->locked = s->count-1;
252
253     return 1;
254 }
255
256 /*---------------------------------------------------------------------------*/
257
258 int set_init()
259 {
260     FILE *fin;
261     char  name[MAXSTR];
262
263     set   = 0;
264     count = 0;
265
266     if ((fin = fopen(config_data(SET_FILE), "r")))
267     {
268         while (count < MAXSET && fgets(name, MAXSTR, fin))
269             if (set_load(&set_v[count], strip_eol(name)))
270                 count++;
271
272         fclose(fin);
273     }
274
275     return count;
276 }
277
278 /*---------------------------------------------------------------------------*/
279
280 int  set_exists(int i)
281 {
282     return (0 <= i && i < count);
283 }
284
285 const struct set *get_set(int i)
286 {
287     return set_exists(i) ? &set_v[i] : NULL;
288 }
289
290 /*---------------------------------------------------------------------------*/
291
292 int set_unlocked(const struct set *s)
293 {
294     return s->locked == 0;
295 }
296
297 int set_completed(const struct set *s)
298 {
299     return s->completed == s->count;
300 }
301
302 int set_level_exists(const struct set *s, int i)
303 {
304     return (i >= 0) && (i < s->count);
305 }
306
307 /*---------------------------------------------------------------------------*/
308
309 static void set_load_levels(void)
310 {
311     FILE *fin;
312
313     struct level *l;
314
315     char buf[MAXSTR];
316     char name[MAXSTR];
317
318     int i = 0, res;
319     int nb = 1, bnb = 1;
320
321     const char *roman[] = {
322         "",
323         "I",   "II",   "III",   "IV",   "V",
324         "VI",  "VII",  "VIII",  "IX",   "X",
325         "XI",  "XII",  "XIII",  "XIV",  "XV",
326         "XVI", "XVII", "XVIII", "XIX",  "XX",
327         "XXI", "XXII", "XXIII", "XXIV", "XXV"
328     };
329
330     if ((fin = fopen(config_data(set_v[set].file), "r")))
331     {
332         res = 1;
333
334         /* Skip the five first lines */
335         for (i = 0; i < 5; i++)
336             fgets(buf, MAXSTR, fin);
337
338         for (i = 0; i < set_v[set].count && res; i++)
339         {
340             l = &level_v[i];
341
342             res = (fscanf(fin, "%s", name) == 1);
343             assert(res);
344
345             level_load(name, l);
346
347             /* Initialize set related info */
348             l->set    = &set_v[set];
349             l->number = i;
350
351             if (l->is_bonus)
352                 sprintf(l->repr, "%s", roman[bnb++]);
353             else
354                 sprintf(l->repr, "%02d", nb++);
355
356             l->is_locked    = 1;
357             l->is_completed = 0;
358         }
359         level_v[0].is_locked = 0; /* unlock the first level */
360         fclose(fin);
361
362         lg->bonusid = 0;
363     }
364
365     assert(i == set_v[set].count);
366 }
367
368 void set_goto(int i)
369 {
370     set = i;
371
372     set_load_levels();
373     set_load_hs();
374 }
375
376 const struct set *curr_set(void)
377 {
378     return &set_v[set];
379 }
380
381 const struct level *get_level(int i)
382 {
383     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
384 }
385
386 /*---------------------------------------------------------------------------*/
387
388 /* Update the level score rank according to coins and timer. */
389 static int level_score_update(struct level_game *lg, const char *player)
390 {
391     int timer = lg->timer;
392     int coins = lg->coins;
393     struct level *l = &level_v[lg->level->number];
394
395     lg->time_rank = score_time_insert(&l->score.best_times,
396                                       player, timer, coins);
397
398     if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
399         lg->goal_rank = score_time_insert(&l->score.unlock_goal,
400                                           player, timer, coins);
401     else
402         lg->goal_rank = 3;
403
404     lg->coin_rank = score_coin_insert(&l->score.most_coins,
405                                       player, timer, coins);
406
407     return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
408 }
409
410 /* Update the set score rank according to score and times. */
411 static int set_score_update(struct level_game *lg, const char *player)
412 {
413     int timer = lg->times;
414     int coins = lg->score;
415     struct set *s = &set_v[set];
416
417     lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
418     lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
419
420     return (lg->score_rank < 3 || lg->times_rank < 3);
421 }
422
423 /* Update the player name for set and level high-score. */
424 void score_change_name(struct level_game *lg, const char *player)
425 {
426     struct set   *s = &set_v[set];
427     struct level *l = &level_v[lg->level->number];
428
429     strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
430     strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
431     strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
432
433     strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
434     strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
435
436     set_store_hs();
437 }
438
439 static struct level *next_level(int i)
440 {
441     return set_level_exists(&set_v[set], i + 1) ? &level_v[i + 1] : NULL;
442 }
443
444 static struct level *next_normal_level(int i)
445 {
446     for (i++; i < set_v[set].count; i++)
447         if (!level_v[i].is_bonus)
448             return &level_v[i];
449
450     return NULL;
451 }
452
453 /*---------------------------------------------------------------------------*/
454
455 void set_finish_level(struct level_game *lg, const char *player)
456 {
457     struct set *s = &set_v[set];
458     int ln = lg->level->number;      /* Current level number       */
459     struct level *cl = &level_v[ln]; /* Current level              */
460     struct level *nl = NULL;         /* Next level                 */
461     int dirty = 0;                   /* Should the score be saved? */
462
463     assert(s == cl->set);
464
465     /* if no set, no next level */
466     if (s == NULL)
467     {
468         /* if no set, return */
469         lg->next_level = NULL;
470         return;
471     }
472
473     /* On level completed */
474     if (lg->status == GAME_GOAL)
475     {
476         /* Update level scores */
477         dirty = level_score_update(lg, player);
478
479         /* Complete the level */
480         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
481         {
482             /* Complete the level */
483             if (!cl->is_completed)
484             {
485                 cl->is_completed = 1;
486                 s->completed += 1;
487                 dirty = 1;
488             }
489         }
490     }
491
492     /* On goal reached */
493     if (lg->status == GAME_GOAL)
494     {
495         /* Identify the following level */
496
497         nl = next_level(ln);
498
499         if (nl != NULL)
500         {
501             /* Skip bonuses if unlocked in any mode */
502
503             if (nl->is_bonus)
504             {
505                 if (lg->mode == MODE_CHALLENGE && nl->is_locked)
506                 {
507                     lg->bonus = 1;
508                     nl->is_locked = 0;
509                 }
510
511                 nl = next_normal_level(nl->number);
512
513                 if (nl == NULL && lg->mode == MODE_CHALLENGE)
514                 {
515                     lg->win = 1;
516                 }
517             }
518         }
519         else if (lg->mode == MODE_CHALLENGE)
520             lg->win = 1;
521     }
522     else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
523     {
524         /* On fail, identify the next level (only in bonus for challenge) */
525         nl = next_normal_level(ln);
526         /* Next level may be unavailable */
527         if (!cl->is_bonus && nl != NULL && nl->is_locked)
528             nl = NULL;
529         /* Fail a bonus level but win the set! */
530         else if (nl == NULL && lg->mode == MODE_CHALLENGE)
531             lg->win = 1;
532     }
533
534     /* Win ! */
535     if (lg->win)
536     {
537         /* update set score */
538         set_score_update(lg, player);
539         /* unlock all levels */
540         set_cheat();
541         dirty = 1;
542     }
543
544     /* unlock the next level if needed */
545     if (nl != NULL && nl->is_locked)
546     {
547         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
548         {
549             lg->unlock = 1;
550             nl->is_locked = 0;
551             s->locked -= 1;
552             dirty = 1;
553         }
554         else
555             nl = NULL;
556     }
557
558     /* got the next level */
559     lg->next_level = nl;
560
561     /* Update file */
562     if (dirty)
563         set_store_hs();
564 }
565
566 /*---------------------------------------------------------------------------*/
567
568 void level_snap(int i)
569 {
570     char filename[MAXSTR];
571     char *ext;
572
573     /* Convert the level name to a PNG filename. */
574
575     memset(filename, 0, MAXSTR);
576
577     ext = strrchr(level_v[i].file, '.');
578     strncpy(filename, level_v[i].file,
579             ext ? ext - level_v[i].file : strlen(level_v[i].file));
580     strcat(filename, ".png");
581
582     /* Initialize the game for a snapshot. */
583
584     if (game_init(&level_v[i], 0, 0))
585     {
586         /* Render the level and grab the screen. */
587
588         config_clear();
589         game_set_fly(1.f);
590         game_kill_fade();
591         game_draw(1, 0);
592         SDL_GL_SwapBuffers();
593
594         image_snap(filename);
595     }
596 }
597
598 void set_cheat(void)
599 /* Open each level of the current set */
600 {
601     int i;
602
603     set_v[set].locked = 0;
604
605     for (i = 0; i < set_v[set].count; i++)
606         level_v[i].is_locked = 0;
607 }
608
609
610 /*---------------------------------------------------------------------------*/