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