Minor clean-up.
[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 /* Update the level score rank according to coins and timer. */
385 static int level_score_update(struct level_game *lg, const char *player)
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 /* Update the set score rank according to score and times. */
407 static int set_score_update(struct level_game *lg, const char *player)
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
416     return (lg->score_rank < 3 || lg->times_rank < 3);
417 }
418
419 /* Update the player name for set and level high-score. */
420 void score_change_name(struct level_game *lg, const char *player)
421 {
422     struct set   *s = &set_v[set];
423     struct level *l = &level_v[lg->level->number];
424
425     strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
426     strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
427     strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
428
429     strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
430     strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
431
432     set_store_hs();
433 }
434
435 static struct level *next_level(int i)
436 {
437     return set_level_exists(&set_v[set], i + 1) ? &level_v[i + 1] : NULL;
438 }
439
440 static struct level *next_normal_level(int i)
441 {
442     for (i++; i < set_v[set].count; i++)
443         if (!level_v[i].is_bonus)
444             return &level_v[i];
445
446     return NULL;
447 }
448
449 /*---------------------------------------------------------------------------*/
450
451 void set_finish_level(struct level_game *lg, const char *player)
452 {
453     struct set *s = &set_v[set];
454     int ln = lg->level->number;      /* Current level number       */
455     struct level *cl = &level_v[ln]; /* Current level              */
456     struct level *nl = NULL;         /* Next level                 */
457     int dirty = 0;                   /* Should the score be saved? */
458
459     assert(s == cl->set);
460
461     /* if no set, no next level */
462     if (s == NULL)
463     {
464         /* if no set, return */
465         lg->next_level = NULL;
466         return;
467     }
468
469     /* On level completed */
470     if (lg->status == GAME_GOAL)
471     {
472         /* Update level scores */
473         dirty = level_score_update(lg, player);
474
475         /* Complete the level */
476         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
477         {
478             /* Complete the level */
479             if (!cl->is_completed)
480             {
481                 cl->is_completed = 1;
482                 s->completed += 1;
483                 dirty = 1;
484             }
485         }
486     }
487
488     /* On goal reached */
489     if (lg->status == GAME_GOAL)
490     {
491         /* Identify the following level */
492         nl = next_level(ln);
493         if (nl != NULL)
494         {
495             /* skip bonuses if unlocked in non challenge mode */
496             if (nl->is_bonus && nl->is_locked && lg->mode != MODE_CHALLENGE)
497                 nl = next_normal_level(nl->number);
498         }
499         else if (lg->mode == MODE_CHALLENGE)
500             lg->win = 1;
501     }
502     else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
503     {
504         /* On fail, identify the next level (only in bonus for challenge) */
505         nl = next_normal_level(ln);
506         /* Next level may be unavailable */
507         if (!cl->is_bonus && nl != NULL && nl->is_locked)
508             nl = NULL;
509         /* Fail a bonus level but win the set! */
510         else if (nl == NULL && lg->mode == MODE_CHALLENGE)
511             lg->win = 1;
512     }
513
514     /* Win ! */
515     if (lg->win)
516     {
517         /* update set score */
518         set_score_update(lg, player);
519         /* unlock all levels */
520         set_cheat();
521         dirty = 1;
522     }
523
524     /* unlock the next level if needed */
525     if (nl != NULL && nl->is_locked)
526     {
527         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
528         {
529             lg->unlock = 1;
530             nl->is_locked = 0;
531             s->locked -= 1;
532             dirty = 1;
533         }
534         else
535             nl = NULL;
536     }
537
538     /* got the next level */
539     lg->next_level = nl;
540
541     /* Update file */
542     if (dirty)
543         set_store_hs();
544 }
545
546 /*---------------------------------------------------------------------------*/
547
548 void level_snap(int i)
549 {
550     char filename[MAXSTR];
551     char *ext;
552
553     /* Convert the level name to a PNG filename. */
554
555     memset(filename, 0, MAXSTR);
556
557     ext = strrchr(level_v[i].file, '.');
558     strncpy(filename, level_v[i].file,
559             ext ? ext - level_v[i].file : strlen(level_v[i].file));
560     strcat(filename, ".png");
561
562     /* Initialize the game for a snapshot. */
563
564     if (game_init(&level_v[i], 0, 0))
565     {
566         int shadow;
567
568         if ((shadow = config_get_d(CONFIG_SHADOW)))
569             config_set_d(CONFIG_SHADOW, 0);
570
571         /* Render the level and grab the screen. */
572
573         config_clear();
574         game_set_fly(1.f);
575         game_kill_fade();
576         game_draw(1, 0);
577         SDL_GL_SwapBuffers();
578
579         image_snap(filename);
580
581         if (shadow)
582             config_set_d(CONFIG_SHADOW, 1);
583     }
584 }
585
586 void set_cheat(void)
587 /* Open each level of the current set */
588 {
589     int i;
590
591     set_v[set].locked = 0;
592
593     for (i = 0; i < set_v[set].count; i++)
594         level_v[i].is_locked = 0;
595 }
596
597
598 /*---------------------------------------------------------------------------*/