Fixed a regression introduced with the previous change. Current set was
[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 set;
29 static int count;
30
31 static struct set set_v[MAXSET];
32 static struct level level_v[MAXLVL];
33
34 /*---------------------------------------------------------------------------*/
35
36 static void put_score(FILE *fp, const struct score *s)
37 {
38     int j;
39
40     for (j = 0; j < NSCORE; j++)
41        fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
42 }
43
44 /* Store the score of the set. */
45 static void set_store_hs(void)
46 {
47     const struct set *s = &set_v[set];
48     FILE *fout;
49     int i;
50     const struct level *l;
51     char states[MAXLVL + 1];
52
53     if ((fout = fopen(config_user(s->user_scores), "w")))
54     {
55         for (i = 0; i < s->count; i++)
56         {
57             if (level_v[i].is_locked)
58                 states[i] = 'L';
59             else if (level_v[i].is_completed)
60                 states[i] = 'C';
61             else
62                 states[i] = 'O';
63         }
64         states[s->count] = '\0';
65         fprintf(fout, "%s\n",states);
66
67         put_score(fout, &s->time_score);
68         put_score(fout, &s->coin_score);
69
70         for (i = 0; i < s->count; i++)
71         {
72             l = &level_v[i];
73
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
88     for (j = 0; j < NSCORE && res; j++)
89     {
90         res = fscanf(fp, "%d %d %s\n",
91                      &s->timer[j],
92                      &s->coins[j],
93                      s->player[j]) == 3;
94     }
95     return res;
96 }
97
98 /* Get the score of the set. */
99 static void set_load_hs(void)
100 {
101     struct set *s = &set_v[set];
102     FILE *fin;
103     int i;
104     int res = 0;
105     struct level *l;
106     const char *fn = config_user(s->user_scores);
107     char states[MAXLVL + 1];
108
109     if ((fin = fopen(fn, "r")))
110     {
111         res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
112
113         for (i = 0; i < s->count && res; i++)
114         {
115             switch (states[i])
116             {
117             case 'L':
118                 level_v[i].is_locked = 1;
119                 level_v[i].is_completed = 0;
120                 break;
121
122             case 'C':
123                 level_v[i].is_locked = 0;
124                 level_v[i].is_completed = 1;
125                 break;
126
127             case 'O':
128                 level_v[i].is_locked = 0;
129                 level_v[i].is_completed = 0;
130                 break;
131
132             default:
133                 res = 0;
134             }
135         }
136
137         res = res &&
138             get_score(fin, &s->time_score) &&
139             get_score(fin, &s->coin_score);
140
141         for (i = 0; i < s->count && res; i++)
142         {
143             l = &level_v[i];
144             res = get_score(fin, &l->score.best_times) &&
145                   get_score(fin, &l->score.unlock_goal) &&
146                   get_score(fin, &l->score.most_coins);
147         }
148
149         fclose(fin);
150     }
151
152     if (!res && errno != ENOENT)
153     {
154         fprintf(stderr,
155                 _("Error while loading user high-score file '%s': %s\n"),
156                 fn, errno ? strerror(errno) : _("Incorrect format"));
157     }
158 }
159
160 static char *strip_eol(char *str)
161 {
162     char *c = str + strlen(str) - 1;
163
164     while (c >= str && (*c == '\n' || *c =='\r'))
165         *c-- = '\0';
166
167     return str;
168 }
169
170 static int set_load(struct set *s, const char *filename)
171 {
172     FILE *fin;
173     char buf[MAXSTR];
174     int res;
175
176     fin = fopen(config_data(filename), "r");
177
178     if (!fin)
179     {
180         fprintf(stderr, _("Cannot load the set file '%s': %s\n"),
181                 filename, strerror(errno));
182         return 0;
183     }
184
185     memset(s, 0, sizeof (struct set));
186
187     /* Set some sane values in case the scores hs is missing. */
188
189     score_init_hs(&s->time_score, 359999, 0);
190     score_init_hs(&s->coin_score, 359999, 0);
191
192     /* Load set metadata. */
193
194     strcpy(s->file, filename);
195
196     if ((res = fgets(buf, MAXSTR, fin) != NULL))
197         strcpy(s->name, strip_eol(buf));
198     if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
199         strcpy(s->desc, strip_eol(buf));
200     if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
201         strcpy(s->id, strip_eol(buf));
202     if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
203         strcpy(s->shot, strip_eol(buf));
204     if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
205         sscanf(buf, "%d %d %d %d %d %d",
206                 &s->time_score.timer[0],
207                 &s->time_score.timer[1],
208                 &s->time_score.timer[2],
209                 &s->coin_score.coins[0],
210                 &s->coin_score.coins[1],
211                 &s->coin_score.coins[2]);
212
213     strcpy(s->user_scores, "neverballhs-");
214     strcat(s->user_scores, s->id);
215
216     /* Count levels. */
217
218     s->count = 0;
219
220     while (s->count < MAXLVL && (fscanf(fin, "%s", buf) == 1))
221         s->count++;
222
223     fclose(fin);
224
225     /* Load the levels states (stored in the user high score file) */
226
227     s->locked = s->count;
228     s->completed = 0;
229
230     if ((fin = fopen(config_user(s->user_scores), "r")))
231     {
232         char states[MAXLVL + 1];
233         int i;
234         if ((fscanf(fin, "%s\n", states) == 1) && (strlen(states) == s->count))
235         {
236             for (i = 0; i < s->count; i++)
237             {
238                 if (states[i] == 'O')
239                     s->locked -= 1;
240                 else if (states[i] == 'C')
241                 {
242                     s->completed += 1;
243                     s->locked -= 1;
244                 }
245             }
246         }
247         fclose(fin);
248     }
249     if (s->locked == s->count)
250         s->locked = s->count-1;
251
252     return 1;
253 }
254
255 /*---------------------------------------------------------------------------*/
256
257 int set_init()
258 {
259     FILE *fin;
260     char  name[MAXSTR];
261
262     set   = 0;
263     count = 0;
264
265     if ((fin = fopen(config_data(SET_FILE), "r")))
266     {
267         while (count < MAXSET && fgets(name, MAXSTR, fin))
268             if (set_load(&set_v[count], strip_eol(name)))
269                 count++;
270
271         fclose(fin);
272     }
273
274     return count;
275 }
276
277 /*---------------------------------------------------------------------------*/
278
279 int  set_exists(int i)
280 {
281     return (0 <= i && i < count);
282 }
283
284 const struct set *get_set(int i)
285 {
286     return set_exists(i) ? &set_v[i] : NULL;
287 }
288
289 /*---------------------------------------------------------------------------*/
290
291 int set_unlocked(const struct set *s)
292 {
293     return s->locked == 0;
294 }
295
296 int set_completed(const struct set *s)
297 {
298     return s->completed == s->count;
299 }
300
301 int set_level_exists(const struct set *s, int i)
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         "",
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(set_v[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 < set_v[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    = &set_v[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 == set_v[set].count);
362 }
363
364 void set_goto(int i)
365 {
366     set = i;
367
368     set_load_levels();
369     set_load_hs();
370 }
371
372 const struct set *curr_set(void)
373 {
374     return &set_v[set];
375 }
376
377 const struct level *get_level(int i)
378 {
379     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
380 }
381
382 /*---------------------------------------------------------------------------*/
383
384 static int level_score_update(struct level_game *lg, const char *player)
385 /* Update the level score rank according to coins and timer */
386 {
387     int timer = lg->timer;
388     int coins = lg->coins;
389     struct level *l = &level_v[lg->level->number];
390
391     lg->time_rank = score_time_insert(&l->score.best_times,
392                                       player, timer, coins);
393
394     if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
395         lg->goal_rank = score_time_insert(&l->score.unlock_goal,
396                                           player, timer, coins);
397     else
398         lg->goal_rank = 3;
399
400     lg->coin_rank = score_coin_insert(&l->score.most_coins,
401                                       player, timer, coins);
402
403     return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
404 }
405
406 static int set_score_update(struct level_game *lg, const char *player)
407 /* Update the set score rank according to score and times */
408 {
409     int timer = lg->times;
410     int coins = lg->score;
411     struct set *s = &set_v[set];
412
413     lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
414     lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
415     return (lg->score_rank < 3 || lg->times_rank < 3);
416 }
417
418
419 void score_change_name(struct level_game *lg, const char *player)
420 /* Update the player name for set and level high-score */
421 {
422 #define UPDATE(i, x) (strncpy((x).player[(i)], player, MAXNAM))
423     struct set *s = &set_v[set];
424     struct level *l = &level_v[lg->level->number];
425     UPDATE(lg->time_rank, l->score.best_times);
426     UPDATE(lg->goal_rank, l->score.unlock_goal);
427     UPDATE(lg->coin_rank, l->score.most_coins);
428     UPDATE(lg->score_rank, s->coin_score);
429     UPDATE(lg->times_rank, s->time_score);
430     set_store_hs();
431 }
432
433 static struct level *next_level(int i)
434 {
435     return set_level_exists(&set_v[set], i + 1) ? &level_v[i + 1] : NULL;
436 }
437
438 static struct level *next_normal_level(int i)
439 /* Return the next normal level (starting for i)
440  * Return NULL if there is not a such level */
441 {
442     for (i++; i < set_v[set].count; i++)
443         if (!level_v[i].is_bonus)
444             return &level_v[i];
445     return NULL;
446 }
447
448 void set_finish_level(struct level_game *lg, const char *player)
449 /* Inform the set that a level is finished.
450  * Update next_level and score rank fields */
451 {
452     struct set *s = &set_v[set];
453     int ln = lg->level->number; /* current level number */
454     struct level *cl = &level_v[ln];    /* current level */
455     struct level *nl = NULL;    /* next level */
456     int dirty = 0;              /* HS should be saved? */
457
458     assert(s == cl->set);
459
460     /* if no set, no next level */
461     if (s == NULL)
462     {
463         /* if no set, return */
464         lg->next_level = NULL;
465         return;
466     }
467
468     /* On level completed */
469     if (lg->state == GAME_GOAL)
470     {
471         /* Update level scores */
472         dirty = level_score_update(lg, player);
473
474         /* Complete the level */
475         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
476         {
477             /* Complete the level */
478             if (!cl->is_completed)
479             {
480                 cl->is_completed = 1;
481                 s->completed += 1;
482                 dirty = 1;
483             }
484         }
485     }
486
487     /* On goal reached */
488     if (lg->state == GAME_GOAL || lg->state == GAME_SPEC)
489     {
490         /* Identify the following level */
491         nl = next_level(ln + lg->state_value);
492         if (nl != NULL)
493         {
494             /* skip bonuses if unlocked in non challenge mode */
495             if (nl->is_bonus && nl->is_locked && lg->mode != MODE_CHALLENGE)
496                 nl = next_normal_level(nl->number);
497         }
498         else if (lg->mode == MODE_CHALLENGE)
499             lg->win = 1;
500     }
501     else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
502     {
503         /* On fail, identify the next level (only in bonus for challenge) */
504         nl = next_normal_level(ln);
505         /* Next level may be unavailable */
506         if (!cl->is_bonus && nl != NULL && nl->is_locked)
507             nl = NULL;
508         /* Fail a bonus level but win the set! */
509         else if (nl == NULL && lg->mode == MODE_CHALLENGE)
510             lg->win = 1;
511     }
512
513     /* Win ! */
514     if (lg->win)
515     {
516         /* update set score */
517         set_score_update(lg, player);
518         /* unlock all levels */
519         set_cheat();
520         dirty = 1;
521     }
522
523     /* unlock the next level if needed */
524     if (nl != NULL && nl->is_locked)
525     {
526         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
527         {
528             lg->unlock = 1;
529             nl->is_locked = 0;
530             s->locked -= 1;
531             dirty = 1;
532         }
533         else
534             nl = NULL;
535     }
536
537     /* got the next level */
538     lg->next_level = nl;
539
540     /* Update file */
541     if (dirty)
542         set_store_hs();
543 }
544
545 /*---------------------------------------------------------------------------*/
546
547 void level_snap(int i)
548 {
549     char filename[MAXSTR];
550     char *ext;
551
552     /* Convert the level name to a PNG filename. */
553
554     memset(filename, 0, MAXSTR);
555
556     ext = strrchr(level_v[i].file, '.');
557     strncpy(filename, level_v[i].file,
558             ext ? ext - level_v[i].file : strlen(level_v[i].file));
559     strcat(filename, ".png");
560
561     /* Initialize the game for a snapshot. */
562
563     if (game_init(&level_v[i], 0, 0))
564     {
565         int shadow;
566
567         if ((shadow = config_get_d(CONFIG_SHADOW)))
568             config_set_d(CONFIG_SHADOW, 0);
569
570         /* Render the level and grab the screen. */
571
572         config_clear();
573         game_set_fly(1.f);
574         game_kill_fade();
575         game_draw(1, 0);
576         SDL_GL_SwapBuffers();
577
578         image_snap(filename);
579
580         if (shadow)
581             config_set_d(CONFIG_SHADOW, 1);
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 /*---------------------------------------------------------------------------*/