A congratulatory message is now given to user when a bonus level is won
[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     struct level *l;
313
314     char buf[MAXSTR];
315     char name[MAXSTR];
316
317     int i = 0, res;
318     int nb = 1, bnb = 1;
319
320     const char *roman[] = {
321         "",
322         "I",   "II",   "III",   "IV",   "V",
323         "VI",  "VII",  "VIII",  "IX",   "X",
324         "XI",  "XII",  "XIII",  "XIV",  "XV",
325         "XVI", "XVII", "XVIII", "XIX",  "XX",
326         "XXI", "XXII", "XXIII", "XXIV", "XXV"
327     };
328
329     if ((fin = fopen(config_data(set_v[set].file), "r")))
330     {
331         res = 1;
332
333         /* Skip the five first lines */
334         for (i = 0; i < 5; i++)
335             fgets(buf, MAXSTR, fin);
336
337         for (i = 0; i < set_v[set].count && res; i++)
338         {
339             l = &level_v[i];
340
341             res = (fscanf(fin, "%s", name) == 1);
342             assert(res);
343
344             level_load(name, l);
345
346             /* Initialize set related info */
347             l->set    = &set_v[set];
348             l->number = i;
349
350             if (l->is_bonus)
351                 sprintf(l->repr, "%s", roman[bnb++]);
352             else
353                 sprintf(l->repr, "%02d", nb++);
354
355             l->is_locked    = 1;
356             l->is_completed = 0;
357         }
358         level_v[0].is_locked = 0; /* unlock the first level */
359         fclose(fin);
360     }
361
362     assert(i == set_v[set].count);
363 }
364
365 void set_goto(int i)
366 {
367     set = i;
368
369     set_load_levels();
370     set_load_hs();
371 }
372
373 const struct set *curr_set(void)
374 {
375     return &set_v[set];
376 }
377
378 const struct level *get_level(int i)
379 {
380     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
381 }
382
383 /*---------------------------------------------------------------------------*/
384
385 /* Update the level score rank according to coins and timer. */
386 static int level_score_update(struct level_game *lg, const char *player)
387 {
388     int timer = lg->timer;
389     int coins = lg->coins;
390     struct level *l = &level_v[lg->level->number];
391
392     lg->time_rank = score_time_insert(&l->score.best_times,
393                                       player, timer, coins);
394
395     if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
396         lg->goal_rank = score_time_insert(&l->score.unlock_goal,
397                                           player, timer, coins);
398     else
399         lg->goal_rank = 3;
400
401     lg->coin_rank = score_coin_insert(&l->score.most_coins,
402                                       player, timer, coins);
403
404     return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
405 }
406
407 /* Update the set score rank according to score and times. */
408 static int set_score_update(struct level_game *lg, const char *player)
409 {
410     int timer = lg->times;
411     int coins = lg->score;
412     struct set *s = &set_v[set];
413
414     lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
415     lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
416
417     return (lg->score_rank < 3 || lg->times_rank < 3);
418 }
419
420 /* Update the player name for set and level high-score. */
421 void score_change_name(struct level_game *lg, const char *player)
422 {
423     struct set   *s = &set_v[set];
424     struct level *l = &level_v[lg->level->number];
425
426     strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
427     strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
428     strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
429
430     strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
431     strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
432
433     set_store_hs();
434 }
435
436 static struct level *next_level(int i)
437 {
438     return set_level_exists(&set_v[set], i + 1) ? &level_v[i + 1] : NULL;
439 }
440
441 static struct level *next_normal_level(int i)
442 {
443     for (i++; i < set_v[set].count; i++)
444         if (!level_v[i].is_bonus)
445             return &level_v[i];
446
447     return NULL;
448 }
449
450 /*---------------------------------------------------------------------------*/
451
452 void set_finish_level(struct level_game *lg, const char *player)
453 {
454     struct set *s = &set_v[set];
455     int ln = lg->level->number;      /* Current level number       */
456     struct level *cl = &level_v[ln]; /* Current level              */
457     struct level *nl = NULL;         /* Next level                 */
458     int dirty = 0;                   /* Should the score be saved? */
459
460     assert(s == cl->set);
461
462     /* if no set, no next level */
463     if (s == NULL)
464     {
465         /* if no set, return */
466         lg->next_level = NULL;
467         return;
468     }
469
470     /* On level completed */
471     if (lg->status == GAME_GOAL)
472     {
473         /* Update level scores */
474         dirty = level_score_update(lg, player);
475
476         /* Complete the level */
477         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
478         {
479             /* Complete the level */
480             if (!cl->is_completed)
481             {
482                 cl->is_completed = 1;
483                 s->completed += 1;
484                 dirty = 1;
485             }
486         }
487     }
488
489     /* On goal reached */
490     if (lg->status == GAME_GOAL)
491     {
492         /* Identify the following level */
493         nl = next_level(ln);
494         if (nl != NULL)
495         {
496             /* skip bonuses if unlocked in any mode */
497             if (nl->is_bonus)
498             {
499                 nl = next_normal_level(nl->number);
500                 if(lg->mode == MODE_CHALLENGE)
501                 {
502                     lg->bonus = 1;
503                 }
504             }
505         }
506         else if (lg->mode == MODE_CHALLENGE)
507             lg->win = 1;
508     }
509     else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
510     {
511         /* On fail, identify the next level (only in bonus for challenge) */
512         nl = next_normal_level(ln);
513         /* Next level may be unavailable */
514         if (!cl->is_bonus && nl != NULL && nl->is_locked)
515             nl = NULL;
516         /* Fail a bonus level but win the set! */
517         else if (nl == NULL && lg->mode == MODE_CHALLENGE)
518             lg->win = 1;
519     }
520
521     /* Win ! */
522     if (lg->win)
523     {
524         /* update set score */
525         set_score_update(lg, player);
526         /* unlock all levels */
527         set_cheat();
528         dirty = 1;
529     }
530
531     /* unlock the next level if needed */
532     if (nl != NULL && nl->is_locked)
533     {
534         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
535         {
536             lg->unlock = 1;
537             nl->is_locked = 0;
538             s->locked -= 1;
539             dirty = 1;
540         }
541         else
542             nl = NULL;
543     }
544
545     /* got the next level */
546     lg->next_level = nl;
547
548     /* Update file */
549     if (dirty)
550         set_store_hs();
551 }
552
553 /*---------------------------------------------------------------------------*/
554
555 void level_snap(int i)
556 {
557     char filename[MAXSTR];
558     char *ext;
559
560     /* Convert the level name to a PNG filename. */
561
562     memset(filename, 0, MAXSTR);
563
564     ext = strrchr(level_v[i].file, '.');
565     strncpy(filename, level_v[i].file,
566             ext ? ext - level_v[i].file : strlen(level_v[i].file));
567     strcat(filename, ".png");
568
569     /* Initialize the game for a snapshot. */
570
571     if (game_init(&level_v[i], 0, 0))
572     {
573         /* Render the level and grab the screen. */
574
575         config_clear();
576         game_set_fly(1.f);
577         game_kill_fade();
578         game_draw(1, 0);
579         SDL_GL_SwapBuffers();
580
581         image_snap(filename);
582     }
583 }
584
585 void set_cheat(void)
586 /* Open each level of the current set */
587 {
588     int i;
589
590     set_v[set].locked = 0;
591
592     for (i = 0; i < set_v[set].count; i++)
593         level_v[i].is_locked = 0;
594 }
595
596
597 /*---------------------------------------------------------------------------*/