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