Set now finishes properly in Challenge mode, Level x renamved to Bonus Level x, Roman...
[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     struct level_game *lg = curr_lg();
365     lg->bonus = lg->bonusid = 0; /* initialize bonus values */
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         nl = next_level(ln);
497         if (nl != NULL)
498         {
499             /* skip bonuses if unlocked in any mode */
500             if (nl->is_bonus)
501             {
502                 if(lg->mode == MODE_CHALLENGE && nl->is_locked > 0)
503                 {
504                     lg->bonus = 1; /* Show GUI message */
505                     nl->is_locked = 0; /* Unlock bonus level */
506                 }
507                 nl = next_normal_level(nl->number);
508                 if(nl == NULL && lg->mode == MODE_CHALLENGE)
509                 {
510                     lg->win = 1;
511                 }
512             }
513         }
514         else if (lg->mode == MODE_CHALLENGE)
515             lg->win = 1;
516     }
517     else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
518     {
519         /* On fail, identify the next level (only in bonus for challenge) */
520         nl = next_normal_level(ln);
521         /* Next level may be unavailable */
522         if (!cl->is_bonus && nl != NULL && nl->is_locked)
523             nl = NULL;
524         /* Fail a bonus level but win the set! */
525         else if (nl == NULL && lg->mode == MODE_CHALLENGE)
526             lg->win = 1;
527     }
528
529     /* Win ! */
530     if (lg->win)
531     {
532         /* update set score */
533         set_score_update(lg, player);
534         /* unlock all levels */
535         set_cheat();
536         dirty = 1;
537     }
538
539     /* unlock the next level if needed */
540     if (nl != NULL && nl->is_locked)
541     {
542         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
543         {
544             lg->unlock = 1;
545             nl->is_locked = 0;
546             s->locked -= 1;
547             dirty = 1;
548         }
549         else
550             nl = NULL;
551     }
552
553     /* got the next level */
554     lg->next_level = nl;
555
556     /* Update file */
557     if (dirty)
558         set_store_hs();
559 }
560
561 /*---------------------------------------------------------------------------*/
562
563 void level_snap(int i)
564 {
565     char filename[MAXSTR];
566     char *ext;
567
568     /* Convert the level name to a PNG filename. */
569
570     memset(filename, 0, MAXSTR);
571
572     ext = strrchr(level_v[i].file, '.');
573     strncpy(filename, level_v[i].file,
574             ext ? ext - level_v[i].file : strlen(level_v[i].file));
575     strcat(filename, ".png");
576
577     /* Initialize the game for a snapshot. */
578
579     if (game_init(&level_v[i], 0, 0))
580     {
581         /* Render the level and grab the screen. */
582
583         config_clear();
584         game_set_fly(1.f);
585         game_kill_fade();
586         game_draw(1, 0);
587         SDL_GL_SwapBuffers();
588
589         image_snap(filename);
590     }
591 }
592
593 void set_cheat(void)
594 /* Open each level of the current set */
595 {
596     int i;
597
598     set_v[set].locked = 0;
599
600     for (i = 0; i < set_v[set].count; i++)
601         level_v[i].is_locked = 0;
602 }
603
604
605 /*---------------------------------------------------------------------------*/