Merged only code for downloaded contribution support from level-contributions
[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     {
326         if(set_contribution(i)!=NULL && !strcmp(set_contribution(i), "0"))
327             return 1;
328     }
329
330     else if (contribValue == 2)
331     {
332         if(set_contribution(i)!=NULL && !strcmp(set_contribution(i), "1"))
333             return 1;
334     }
335
336     else
337         return (0 <= i && i < count);
338
339     return 0;
340 }
341
342 const char *set_name(int i)
343 {
344     return set_exists(i, 0) ? _(set_v[i].name) : NULL;
345 }
346
347 const char *set_desc(int i)
348 {
349     return set_exists(i, 0) ? _(set_v[i].desc) : NULL;
350 }
351
352 const char *set_shot(int i)
353 {
354     return set_exists(i, 0) ? set_v[i].shot : NULL;
355 }
356
357 const struct score *set_time_score(int i)
358 {
359     return set_exists(i, 0) ? &set_v[i].time_score : NULL;
360 }
361
362 const struct score *set_coin_score(int i)
363 {
364     return set_exists(i, 0) ? &set_v[i].coin_score : NULL;
365 }
366
367 const char *set_contribution(int i)
368 {
369     return set_exists(i, 0) ? _(set_v[i].contribution) : NULL;
370 }
371
372 /*---------------------------------------------------------------------------*/
373
374 int set_level_exists(int s, int i)
375 {
376     return (i >= 0 && i < set_v[s].count);
377 }
378
379 static void set_load_levels(void)
380 {
381     struct level *l;
382     int nb = 1, bnb = 1;
383
384     int i;
385
386     const char *roman[] = {
387         "",
388         "I",   "II",   "III",   "IV",   "V",
389         "VI",  "VII",  "VIII",  "IX",   "X",
390         "XI",  "XII",  "XIII",  "XIV",  "XV",
391         "XVI", "XVII", "XVIII", "XIX",  "XX",
392         "XXI", "XXII", "XXIII", "XXIV", "XXV"
393     };
394
395     for (i = 0; i < set_v[set].count; i++)
396     {
397         l = &level_v[i];
398
399         level_load(set_v[set].level_name_v[i], l);
400
401         l->set    = &set_v[set];
402         l->number = i;
403
404         if (l->is_bonus)
405             sprintf(l->repr, "%s",   roman[bnb++]);
406         else
407             sprintf(l->repr, "%02d", nb++);
408
409         l->is_locked    = 1;
410         l->is_completed = 0;
411     }
412
413     /* Unlock first level. */
414
415     level_v[0].is_locked = 0;
416 }
417
418 void set_goto(int i)
419 {
420     set = i;
421
422     set_load_levels();
423     set_load_hs();
424 }
425
426 int curr_set(void)
427 {
428     return set;
429 }
430
431 const struct level *get_level(int i)
432 {
433     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
434 }
435
436 /*---------------------------------------------------------------------------*/
437
438 /* Update the level score rank according to coins and timer. */
439 static int level_score_update(struct level_game *lg, const char *player)
440 {
441     int timer = lg->timer;
442     int coins = lg->coins;
443     struct level *l = &level_v[lg->level->number];
444
445     lg->time_rank = score_time_insert(&l->score.best_times,
446                                       player, timer, coins);
447
448     if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
449         lg->goal_rank = score_time_insert(&l->score.unlock_goal,
450                                           player, timer, coins);
451     else
452         lg->goal_rank = 3;
453
454     lg->coin_rank = score_coin_insert(&l->score.most_coins,
455                                       player, timer, coins);
456
457     return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
458 }
459
460 /* Update the set score rank according to score and times. */
461 static int set_score_update(struct level_game *lg, const char *player)
462 {
463     int timer = lg->times;
464     int coins = lg->score;
465     struct set *s = &set_v[set];
466
467     lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
468     lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
469
470     return (lg->score_rank < 3 || lg->times_rank < 3);
471 }
472
473 /* Update the player name for set and level high-score. */
474 void score_change_name(struct level_game *lg, const char *player)
475 {
476     struct set   *s = &set_v[set];
477     struct level *l = &level_v[lg->level->number];
478
479     strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
480     strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
481     strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
482
483     strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
484     strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
485
486     set_store_hs();
487 }
488
489 static struct level *next_level(int i)
490 {
491     return set_level_exists(set, i + 1) ? &level_v[i + 1] : NULL;
492 }
493
494 static struct level *next_normal_level(int i)
495 {
496     for (i++; i < set_v[set].count; i++)
497         if (!level_v[i].is_bonus)
498             return &level_v[i];
499
500     return NULL;
501 }
502
503 /*---------------------------------------------------------------------------*/
504
505 void set_finish_level(struct level_game *lg, const char *player)
506 {
507     struct set *s = &set_v[set];
508     int ln = lg->level->number;      /* Current level number       */
509     struct level *cl = &level_v[ln]; /* Current level              */
510     struct level *nl = NULL;         /* Next level                 */
511     int dirty = 0;                   /* Should the score be saved? */
512
513     assert(s == cl->set);
514
515     /* if no set, no next level */
516     if (s == NULL)
517     {
518         /* if no set, return */
519         lg->next_level = NULL;
520         return;
521     }
522
523     /* On level completed */
524     if (lg->status == GAME_GOAL)
525     {
526         /* Update level scores */
527         dirty = level_score_update(lg, player);
528
529         /* Complete the level */
530         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
531         {
532             /* Complete the level */
533             if (!cl->is_completed)
534             {
535                 cl->is_completed = 1;
536                 dirty = 1;
537             }
538         }
539     }
540
541     /* On goal reached */
542     if (lg->status == GAME_GOAL)
543     {
544         /* Identify the following level */
545
546         nl = next_level(ln);
547
548         if (nl != NULL)
549         {
550             /* Skip bonuses if unlocked in any mode */
551
552             if (nl->is_bonus)
553             {
554                 if (lg->mode == MODE_CHALLENGE && nl->is_locked)
555                 {
556                     nl->is_locked = 0;
557
558                     lg->bonus = 1;
559                     lg->bonus_repr = nl->repr;
560                 }
561
562                 nl = next_normal_level(nl->number);
563
564                 if (nl == NULL && lg->mode == MODE_CHALLENGE)
565                 {
566                     lg->win = 1;
567                 }
568             }
569         }
570         else if (lg->mode == MODE_CHALLENGE)
571             lg->win = 1;
572     }
573     else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
574     {
575         /* On fail, identify the next level (only in bonus for challenge) */
576         nl = next_normal_level(ln);
577         /* Next level may be unavailable */
578         if (!cl->is_bonus && nl != NULL && nl->is_locked)
579             nl = NULL;
580         /* Fail a bonus level but win the set! */
581         else if (nl == NULL && lg->mode == MODE_CHALLENGE)
582             lg->win = 1;
583     }
584
585     /* Win ! */
586     if (lg->win)
587     {
588         /* update set score */
589         set_score_update(lg, player);
590         /* unlock all levels */
591         set_cheat();
592         dirty = 1;
593     }
594
595     /* unlock the next level if needed */
596     if (nl != NULL && nl->is_locked)
597     {
598         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
599         {
600             lg->unlock = 1;
601             nl->is_locked = 0;
602             dirty = 1;
603         }
604         else
605             nl = NULL;
606     }
607
608     /* got the next level */
609     lg->next_level = nl;
610
611     /* Update file */
612     if (dirty)
613         set_store_hs();
614 }
615
616 /*---------------------------------------------------------------------------*/
617
618 void level_snap(int i)
619 {
620     char filename[MAXSTR];
621     char *ext;
622
623     /* Convert the level name to a PNG filename. */
624
625     memset(filename, 0, MAXSTR);
626
627     ext = strrchr(level_v[i].file, '.');
628     strncpy(filename, level_v[i].file,
629             ext ? ext - level_v[i].file : strlen(level_v[i].file));
630     strcat(filename, ".png");
631
632     /* Initialize the game for a snapshot. */
633
634     if (game_init(&level_v[i], 0, 0))
635     {
636         /* Render the level and grab the screen. */
637
638         config_clear();
639         game_set_fly(1.f);
640         game_kill_fade();
641         game_draw(1, 0);
642         SDL_GL_SwapBuffers();
643
644         image_snap(filename);
645     }
646 }
647
648 void set_cheat(void)
649 {
650     int i;
651
652     for (i = 0; i < set_v[set].count; i++)
653         level_v[i].is_locked = 0;
654 }
655
656 /*---------------------------------------------------------------------------*/