Those sets haven't been merged.
[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
363     assert(i == set_v[set].count);
364 }
365
366 void set_goto(int i)
367 {
368     set = i;
369
370     set_load_levels();
371     set_load_hs();
372 }
373
374 const struct set *curr_set(void)
375 {
376     return &set_v[set];
377 }
378
379 const struct level *get_level(int i)
380 {
381     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
382 }
383
384 /*---------------------------------------------------------------------------*/
385
386 /* Update the level score rank according to coins and timer. */
387 static int level_score_update(struct level_game *lg, const char *player)
388 {
389     int timer = lg->timer;
390     int coins = lg->coins;
391     struct level *l = &level_v[lg->level->number];
392
393     lg->time_rank = score_time_insert(&l->score.best_times,
394                                       player, timer, coins);
395
396     if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
397         lg->goal_rank = score_time_insert(&l->score.unlock_goal,
398                                           player, timer, coins);
399     else
400         lg->goal_rank = 3;
401
402     lg->coin_rank = score_coin_insert(&l->score.most_coins,
403                                       player, timer, coins);
404
405     return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
406 }
407
408 /* Update the set score rank according to score and times. */
409 static int set_score_update(struct level_game *lg, const char *player)
410 {
411     int timer = lg->times;
412     int coins = lg->score;
413     struct set *s = &set_v[set];
414
415     lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
416     lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
417
418     return (lg->score_rank < 3 || lg->times_rank < 3);
419 }
420
421 /* Update the player name for set and level high-score. */
422 void score_change_name(struct level_game *lg, const char *player)
423 {
424     struct set   *s = &set_v[set];
425     struct level *l = &level_v[lg->level->number];
426
427     strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
428     strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
429     strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
430
431     strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
432     strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
433
434     set_store_hs();
435 }
436
437 static struct level *next_level(int i)
438 {
439     return set_level_exists(&set_v[set], i + 1) ? &level_v[i + 1] : NULL;
440 }
441
442 static struct level *next_normal_level(int i)
443 {
444     for (i++; i < set_v[set].count; i++)
445         if (!level_v[i].is_bonus)
446             return &level_v[i];
447
448     return NULL;
449 }
450
451 /*---------------------------------------------------------------------------*/
452
453 void set_finish_level(struct level_game *lg, const char *player)
454 {
455     struct set *s = &set_v[set];
456     int ln = lg->level->number;      /* Current level number       */
457     struct level *cl = &level_v[ln]; /* Current level              */
458     struct level *nl = NULL;         /* Next level                 */
459     int dirty = 0;                   /* Should the score be saved? */
460
461     assert(s == cl->set);
462
463     /* if no set, no next level */
464     if (s == NULL)
465     {
466         /* if no set, return */
467         lg->next_level = NULL;
468         return;
469     }
470
471     /* On level completed */
472     if (lg->status == GAME_GOAL)
473     {
474         /* Update level scores */
475         dirty = level_score_update(lg, player);
476
477         /* Complete the level */
478         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
479         {
480             /* Complete the level */
481             if (!cl->is_completed)
482             {
483                 cl->is_completed = 1;
484                 s->completed += 1;
485                 dirty = 1;
486             }
487         }
488     }
489
490     /* On goal reached */
491     if (lg->status == GAME_GOAL)
492     {
493         /* Identify the following level */
494
495         nl = next_level(ln);
496
497         if (nl != NULL)
498         {
499             /* Skip bonuses if unlocked in any mode */
500
501             if (nl->is_bonus)
502             {
503                 if (lg->mode == MODE_CHALLENGE && nl->is_locked)
504                 {
505                     nl->is_locked = 0;
506
507                     lg->bonus = 1;
508                     lg->bonus_repr = nl->repr;
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 /*---------------------------------------------------------------------------*/