Fix high-score saving.
[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(const struct set *s)
46 /* Store the score of the set */
47 {
48     FILE *fout;
49     int i;
50     const struct set_level * sl;
51     int l = s->limit;
52     
53     if (l <= s->count)
54         l = s->count - 1;
55
56     if ((fout = fopen(config_user(s->user_scores), "w")))
57     {
58         fprintf(fout, "%d\n", s->limit);
59         
60         put_score(fout, &s->time_score);
61         put_score(fout, &s->coin_score);
62
63         for (i = 0 ; i <= l ; i++)
64         {
65             sl = &s->levels[i];
66             put_score(fout, &sl->time_score);
67             put_score(fout, &sl->goal_score);
68             put_score(fout, &sl->coin_score);
69         }
70
71         fclose(fout);
72     }
73 }
74
75 static int get_score(FILE *fp, struct score *s)
76 {
77     int j;
78     int res = 1;
79     for (j = 0; j < NSCORE && res; j++)
80     {
81        res = (fscanf(fp, "%d %d %s\n",
82                &s->timer[j], &s->coins[j], s->player[j])) == 3;
83     }
84     return res;
85 }
86
87 static void set_load_hs(struct set *s)
88 /* Get the score of the set and set the limit */
89 {
90     FILE *fin;
91     int i;
92     int res = 0;
93     struct set_level * sl;
94     const char *fn = config_user(s->user_scores);
95     int l;
96
97     if ((fin = fopen(fn, "r")))
98     {
99         res = (fscanf(fin, "%d\n", &l) == 1) &&
100             s->limit == l &&
101             get_score(fin, &s->time_score) &&
102             get_score(fin, &s->coin_score);
103         
104         if (l >= s->count)
105             l = s->count - 1;
106         
107         for (i = 0; i <= l && res; i++)
108         {
109             sl = &s->levels[i];
110             res = get_score(fin, &sl->time_score) &&
111                   get_score(fin, &sl->goal_score) &&
112                   get_score(fin, &sl->coin_score);
113         }
114
115         fclose(fin);
116     }
117     
118     if (!res && errno != ENOENT)
119     {
120         fprintf(stderr, _("Error while loading user high-score file '%s': "), fn);
121         if (errno)
122             perror(NULL);
123         else
124             fprintf(stderr, _("Incorrect format\n"));
125     }
126 }
127
128 static const char * numbernames[] = {
129         "01", "02", "03", "04", "05",
130         "06", "07", "08", "09", "10",
131         "11", "12", "13", "14", "15",
132         "16", "17", "18", "19", "20",
133         N_("B1"), N_("B2"), N_("B3"), N_("B4"), N_("B5")};
134
135
136 static void set_init_levels(struct set *s)
137 /* Count levels */
138 {
139     FILE *fin;
140     char buf[MAXSTR];
141     struct set_level * sl;
142
143     /* Load the levels list. */
144     
145     s->count = 0;
146
147     if ((fin = fopen(config_data(s->init_levels), "r")))
148     {
149         while (count < MAXLVL && fgets(buf, MAXSTR, fin))
150         {
151             sl = &s->levels[s->count];
152             sl->numbername = numbernames[s->count];
153             s->count++;
154         }
155         fclose(fin);
156     }
157
158     /* Load the highscore level limit */
159     
160     s->limit = 0;
161     
162     if ((fin = fopen(config_user(s->user_scores), "r")))
163     {
164         fscanf(fin, "%d\n", &s->limit);
165         if (s->limit > s->count)
166             s->limit = 0;
167         fclose(fin);
168     }
169     
170 }
171
172 static void score_init_hs(struct score *s, int timer, int coins)
173 {
174     int i;
175     strcpy(s->player[0], "Hard");
176     strcpy(s->player[1], "Medium");
177     strcpy(s->player[2], "Easy");
178     for (i = 0; i < NSCORE; i++)
179     {
180         s->timer[i] = timer;
181         s->coins[i] = coins;
182     }
183     
184 }
185
186 static void set_init_hs(struct set *s)
187 {
188     char buf[MAXSTR];
189     FILE *fin;
190     int i = 0;
191     int res = 0;
192     struct set_level * sl;
193     const char *fn = config_data(s->init_scores);
194
195     /* Set some sane values in case the scores file is missing. */
196     score_init_hs(&s->time_score, 359999, 0);
197     score_init_hs(&s->coin_score, 359999, 0);
198
199     for (i = 0; i < s->count; i++)
200     {
201             sl = &s->levels[i];
202             score_init_hs(&sl->time_score, 59999, 0);
203             score_init_hs(&sl->goal_score, 59999, 0);
204             score_init_hs(&sl->coin_score, 59999, 0);
205     }
206
207     /* Load the default high scores file. */
208
209     if ((fin = fopen(fn, "r")))
210     {
211         res = fgets(buf, MAXSTR, fin) != NULL;
212         
213         res = res && (sscanf(buf, "%d %d %d %d %d %d",
214                     &s->time_score.timer[0],
215                     &s->coin_score.coins[0],
216                     &s->time_score.timer[1],
217                     &s->coin_score.coins[1],
218                     &s->time_score.timer[2],
219                     &s->coin_score.coins[2]) == 6);
220         
221         for (i = 0; i < s->count && res; i++)
222         {
223             sl = &s->levels[i];
224             res = (fgets(buf, MAXSTR, fin) != NULL) &&
225                 sscanf(buf, "%d %d %d %d %d %d %d %d %d",
226                         &(sl->time_score.timer[0]),
227                         &(sl->goal_score.timer[0]),
228                         &(sl->coin_score.coins[0]),
229                         &(sl->time_score.timer[1]),
230                         &(sl->goal_score.timer[1]),
231                         &(sl->coin_score.coins[1]),
232                         &(sl->time_score.timer[2]),
233                         &(sl->goal_score.timer[2]),
234                         &(sl->coin_score.coins[2])) == 9;
235         }
236
237         fclose(fin);
238     }
239     
240     if (!res)
241     {
242         fprintf(stderr, _("Error while loading initial high-score file '%s': "), fn);
243         if (errno)
244             perror(NULL);
245         else
246             fprintf(stderr, _("Incorrect format\n"));
247     }
248 }
249
250 /*---------------------------------------------------------------------------*/
251
252 void set_init()
253 {
254     FILE *fin;
255     struct set * set;
256     int res;
257
258     current_set = NULL;
259     
260     count = 0;
261
262     if ((fin = fopen(config_data(SET_FILE), "r")))
263     {
264         res = 1;
265         while (count < MAXSET && res)
266         {
267             set = &(set_v[count]);
268
269             /* clean the set data */
270             memset(set, 0, sizeof(struct set));
271             
272             res = fscanf(fin, "%s %s %s %s\n",
273                       set->init_levels,
274                       set->init_scores,
275                       set->user_scores,
276                       set->shot) == 4 &&
277                fgets(set->name, MAXSTR, fin) &&
278                fgets(set->desc, MAXSTR, fin);
279             if (res)
280             {
281                 char *p = set->name + strlen(set->name) - 1;
282                 char *q = set->desc + strlen(set->desc) - 1;
283                 set->number = count;
284
285                 if (*p == '\n') *p = 0;
286                 if (*q == '\n') *q = 0;
287
288                 set_init_levels(set);
289            
290                 count++;
291             }
292         }
293
294         fclose(fin);
295     }
296 }
297
298 /*---------------------------------------------------------------------------*/
299
300 int  set_exists(int i)
301 {
302     return (0 <= i && i < count);
303 }
304
305 const struct set *get_set(int i)
306 {
307     return set_exists(i) ? &set_v[i] : NULL;
308 }
309
310 /*---------------------------------------------------------------------------*/
311
312 int  set_extra_bonus_opened(const struct set *s)
313 /* Are extra bonus openned (ie challenge completed)? */
314 {
315     return s->limit >= 20;
316 }
317
318 int  set_completed(const struct set *s)
319 /* Are all levels (even extra bonus) completed? */
320 {
321     return s->limit >= s->count;
322 }
323
324 /*---------------------------------------------------------------------------*/
325
326 int  set_level_exists(const struct set *s, int i)
327 /* Is the level i of the set exists */
328 {
329     return (i >= 0) && (i < s->count);
330 }
331
332 int  set_level_opened(const struct set *s, int i)
333 /* Is the level i of the set completed? */
334 {
335     return (i >= 0) && (i <= s->limit);
336 }
337
338 int  set_level_extra_bonus(const struct set *s, int i)
339 /* Is the level i of the set a extra-bonus level? */
340 {
341     return (i >= 20) && (i < s->count);
342 }
343
344 /*---------------------------------------------------------------------------*/
345
346 static void set_load_levels(void)
347 /* Load more the levels of the current set */
348 {
349     FILE *fin;
350     char buf[MAXSTR];
351     char name[MAXSTR];
352     struct level * l;
353
354     int i=0, res;
355     
356     if ((fin = fopen(config_data(current_set->init_levels), "r")))
357     {
358         res = 1;
359         for(i=0; i<current_set->count && res; i++)
360         {
361             l = &level_v[i];
362             res = (fgets(buf, MAXSTR, fin) != NULL) &&
363                 (sscanf(buf, "%s %s %s %s %d %d %s",
364                         name,
365                         l->back,
366                         l->shot,
367                         l->grad,
368                         &l->time,
369                         &l->goal,
370                         l->song) == 7);
371             assert(res);
372             level_load(config_data(name), l);
373         }
374         fclose(fin);
375     }
376     assert(i == current_set->count);
377 }
378
379 void set_goto(int i)
380 {
381     assert(set_exists(i));
382     current_set = &set_v[i];
383     set_init_hs(current_set);
384     set_load_hs(current_set);
385     set_load_levels();
386 }
387
388 const struct set *curr_set(void)
389 {
390     return current_set;
391 }
392
393 const struct level *get_level(int i)
394 {
395     return (i>=0 && i<current_set->count) ? &level_v[i] : NULL;
396 }
397
398 /*---------------------------------------------------------------------------*/
399
400 static int score_time_comp(const struct score *S, int i, int j)
401 {
402     if (S->timer[i] <  S->timer[j])
403         return 1;
404
405     if (S->timer[i] == S->timer[j] &&
406         S->coins[i] >  S->coins[j])
407         return 1;
408
409     return 0;
410 }
411
412 static int score_coin_comp(const struct score *S, int i, int j)
413 {
414     if (S->coins[i] >  S->coins[j])
415         return 1;
416
417     if (S->coins[i] == S->coins[j] &&
418         S->timer[i] <  S->timer[j])
419         return 1;
420
421     return 0;
422 }
423
424 static void score_swap(struct score *S, int i, int j)
425 {
426     char player[MAXNAM];
427     int  tmp;
428
429     strncpy(player,       S->player[i], MAXNAM);
430     strncpy(S->player[i], S->player[j], MAXNAM);
431     strncpy(S->player[j], player,       MAXNAM);
432
433     tmp         = S->timer[i];
434     S->timer[i] = S->timer[j];
435     S->timer[j] = tmp;
436
437     tmp         = S->coins[i];
438     S->coins[i] = S->coins[j];
439     S->coins[j] = tmp;
440 }
441
442 static int score_time_insert(struct score *s, const char* player, int timer, int coins)
443 {
444     int i;
445     
446     strncpy(s->player[3], player, MAXNAM);
447     s->timer[3] = timer;
448     s->coins[3] = coins;
449
450     for (i = 2; i >= 0 && score_time_comp(s, i + 1, i); i--)
451         score_swap(s, i + 1, i);
452     return i+1;
453 }
454
455 static int score_coin_insert(struct score *s, const char* player, int timer, int coins)
456 {
457     int i;
458     
459     strncpy(s->player[3], player, MAXNAM);
460     s->timer[3] = timer;
461     s->coins[3] = coins;
462
463     for (i = 2; i >= 0 && score_coin_comp(s, i + 1, i); i--)
464         score_swap(s, i + 1, i);
465     return i+1;
466 }
467
468 static int level_score_update(struct level_game *lg, const char *player)
469 /* Update the level score rank according to coins and timer */
470 {
471     int timer = lg->timer;
472     int coins = lg->coins;
473     struct set_level * sl = &current_set->levels[lg->level];
474
475     lg->time_rank = score_time_insert(&sl->time_score, player, timer, coins);
476         
477     if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
478         lg->goal_rank = score_time_insert(&sl->goal_score, player, timer, coins);
479     else
480         lg->goal_rank = 3;
481
482     lg->coin_rank = score_coin_insert(&sl->coin_score, player, timer, coins);
483
484     return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
485 }
486
487 static int set_score_update(struct level_game *lg, const char *player)
488 /* Update the set score rank according to score and times */
489 {
490     int timer = lg->times;
491     int coins = lg->score;
492     struct set * s = current_set;
493
494     lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
495     lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
496     return (lg->score_rank < 3 || lg->times_rank < 3);
497 }
498
499
500 void score_change_name(struct level_game *lg, const char *player)
501 /* Update the player name for set and level high-score */
502 {
503 #define UPDATE(i, x) (strncpy((x).player[(i)], player, MAXNAM))
504     struct set * s = current_set;
505     struct set_level *l = &s->levels[lg->level];
506     UPDATE(lg->time_rank, l->time_score);
507     UPDATE(lg->goal_rank, l->goal_score);
508     UPDATE(lg->coin_rank, l->coin_score);
509     UPDATE(lg->score_rank, s->coin_score);
510     UPDATE(lg->times_rank, s->time_score);
511     set_store_hs(s);
512 }
513
514 void set_finish_level(struct level_game *lg, const char *player)
515 /* Inform the set that a level is finished. 
516  * Update next_level and score rank fields */
517 {
518     struct set *s = current_set;
519     int level = lg->level;
520     int dirty = 0;
521     
522     /* Update scores */
523     dirty = level_score_update(lg, player);
524     dirty = set_score_update(lg, player) || dirty;
525     
526     /* compute the next level */    
527     if (s == NULL)
528     {
529         /* if no set, return */
530         lg->next_level = -1;
531         return;
532     }
533    
534     level++; /* level is the next level */
535     
536     /* if the next level is not oppened */
537     if (s->limit < level)
538         if ((lg->mode == MODE_CHALLENGE) ||
539                 (lg->mode == MODE_NORMAL && (level < 20 || level > 20)))
540         {
541             s->limit = level;
542             dirty = 1;
543         }      
544    
545     /* got the next level */ 
546     if (lg->mode == MODE_CHALLENGE && level >= 20)
547         lg->next_level = -1; /* End the challenge */
548     else if (level < s->count && level <= s->limit)
549         lg->next_level = level;
550     else
551         lg->next_level = -1;
552
553     /* Update file */
554     if (dirty)
555         set_store_hs(s);
556 }
557
558 /*---------------------------------------------------------------------------*/
559
560 void level_snap(int i)
561 {
562     char filename[MAXSTR];
563
564     /* Convert the level name to a BMP filename. */
565
566     memset(filename, 0, MAXSTR);
567     strncpy(filename, level_v[i].file, strcspn(level_v[i].file, "."));
568     strcat(filename, ".bmp");
569
570     /* Initialize the game for a snapshot. */
571
572     if (game_init(&level_v[i], 0, 0))
573     {
574         /* Render the level and grab the screen. */
575
576         config_clear();
577         game_set_fly(1.f);
578         game_kill_fade();
579         game_draw(1, 0);
580         SDL_GL_SwapBuffers();
581
582         image_snap(filename, config_get_d(CONFIG_WIDTH), config_get_d(CONFIG_HEIGHT));
583     }
584 }
585
586 void set_cheat(void)
587 /* Open each level of the current set */
588 {
589     current_set->limit = current_set->count;
590 }
591
592
593 /*---------------------------------------------------------------------------*/
594