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