ball/set.c: renamed set_count back to count; changed way of
[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_load_levels();
367     set_load_hs();
368 }
369
370 const struct set *curr_set(void)
371 {
372     return &set_v[set];
373 }
374
375 const struct level *get_level(int i)
376 {
377     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
378 }
379
380 /*---------------------------------------------------------------------------*/
381
382 static int level_score_update(struct level_game *lg, const char *player)
383 /* Update the level score rank according to coins and timer */
384 {
385     int timer = lg->timer;
386     int coins = lg->coins;
387     struct level *l = &level_v[lg->level->number];
388
389     lg->time_rank = score_time_insert(&l->score.best_times,
390                                       player, timer, coins);
391
392     if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
393         lg->goal_rank = score_time_insert(&l->score.unlock_goal,
394                                           player, timer, coins);
395     else
396         lg->goal_rank = 3;
397
398     lg->coin_rank = score_coin_insert(&l->score.most_coins,
399                                       player, timer, coins);
400
401     return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
402 }
403
404 static int set_score_update(struct level_game *lg, const char *player)
405 /* Update the set score rank according to score and times */
406 {
407     int timer = lg->times;
408     int coins = lg->score;
409     struct set *s = &set_v[set];
410
411     lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
412     lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
413     return (lg->score_rank < 3 || lg->times_rank < 3);
414 }
415
416
417 void score_change_name(struct level_game *lg, const char *player)
418 /* Update the player name for set and level high-score */
419 {
420 #define UPDATE(i, x) (strncpy((x).player[(i)], player, MAXNAM))
421     struct set *s = &set_v[set];
422     struct level *l = &level_v[lg->level->number];
423     UPDATE(lg->time_rank, l->score.best_times);
424     UPDATE(lg->goal_rank, l->score.unlock_goal);
425     UPDATE(lg->coin_rank, l->score.most_coins);
426     UPDATE(lg->score_rank, s->coin_score);
427     UPDATE(lg->times_rank, s->time_score);
428     set_store_hs();
429 }
430
431 static struct level *next_level(int i)
432 {
433     return set_level_exists(&set_v[set], i + 1) ? &level_v[i + 1] : NULL;
434 }
435
436 static struct level *next_normal_level(int i)
437 /* Return the next normal level (starting for i)
438  * Return NULL if there is not a such level */
439 {
440     for (i++; i < set_v[set].count; i++)
441         if (!level_v[i].is_bonus)
442             return &level_v[i];
443     return NULL;
444 }
445
446 void set_finish_level(struct level_game *lg, const char *player)
447 /* Inform the set that a level is finished.
448  * Update next_level and score rank fields */
449 {
450     struct set *s = &set_v[set];
451     int ln = lg->level->number; /* current level number */
452     struct level *cl = &level_v[ln];    /* current level */
453     struct level *nl = NULL;    /* next level */
454     int dirty = 0;              /* HS should be saved? */
455
456     assert(s == cl->set);
457
458     /* if no set, no next level */
459     if (s == NULL)
460     {
461         /* if no set, return */
462         lg->next_level = NULL;
463         return;
464     }
465
466     /* On level completed */
467     if (lg->state == GAME_GOAL)
468     {
469         /* Update level scores */
470         dirty = level_score_update(lg, player);
471
472         /* Complete the level */
473         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
474         {
475             /* Complete the level */
476             if (!cl->is_completed)
477             {
478                 cl->is_completed = 1;
479                 s->completed += 1;
480                 dirty = 1;
481             }
482         }
483     }
484
485     /* On goal reached */
486     if (lg->state == GAME_GOAL || lg->state == GAME_SPEC)
487     {
488         /* Identify the following level */
489         nl = next_level(ln + lg->state_value);
490         if (nl != NULL)
491         {
492             /* skip bonuses if unlocked in non challenge mode */
493             if (nl->is_bonus && nl->is_locked && lg->mode != MODE_CHALLENGE)
494                 nl = next_normal_level(nl->number);
495         }
496         else if (lg->mode == MODE_CHALLENGE)
497             lg->win = 1;
498     }
499     else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
500     {
501         /* On fail, identify the next level (only in bonus for challenge) */
502         nl = next_normal_level(ln);
503         /* Next level may be unavailable */
504         if (!cl->is_bonus && nl != NULL && nl->is_locked)
505             nl = NULL;
506         /* Fail a bonus level but win the set! */
507         else if (nl == NULL && lg->mode == MODE_CHALLENGE)
508             lg->win = 1;
509     }
510
511     /* Win ! */
512     if (lg->win)
513     {
514         /* update set score */
515         set_score_update(lg, player);
516         /* unlock all levels */
517         set_cheat();
518         dirty = 1;
519     }
520
521     /* unlock the next level if needed */
522     if (nl != NULL && nl->is_locked)
523     {
524         if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
525         {
526             lg->unlock = 1;
527             nl->is_locked = 0;
528             s->locked -= 1;
529             dirty = 1;
530         }
531         else
532             nl = NULL;
533     }
534
535     /* got the next level */
536     lg->next_level = nl;
537
538     /* Update file */
539     if (dirty)
540         set_store_hs();
541 }
542
543 /*---------------------------------------------------------------------------*/
544
545 void level_snap(int i)
546 {
547     char filename[MAXSTR];
548     char *ext;
549
550     /* Convert the level name to a PNG filename. */
551
552     memset(filename, 0, MAXSTR);
553
554     ext = strrchr(level_v[i].file, '.');
555     strncpy(filename, level_v[i].file,
556             ext ? ext - level_v[i].file : strlen(level_v[i].file));
557     strcat(filename, ".png");
558
559     /* Initialize the game for a snapshot. */
560
561     if (game_init(&level_v[i], 0, 0))
562     {
563         int shadow;
564
565         if ((shadow = config_get_d(CONFIG_SHADOW)))
566             config_set_d(CONFIG_SHADOW, 0);
567
568         /* Render the level and grab the screen. */
569
570         config_clear();
571         game_set_fly(1.f);
572         game_kill_fade();
573         game_draw(1, 0);
574         SDL_GL_SwapBuffers();
575
576         image_snap(filename);
577
578         if (shadow)
579             config_set_d(CONFIG_SHADOW, 1);
580     }
581 }
582
583 void set_cheat(void)
584 /* Open each level of the current set */
585 {
586     int i;
587
588     set_v[set].locked = 0;
589
590     for (i = 0; i < set_v[set].count; i++)
591         level_v[i].is_locked = 0;
592 }
593
594
595 /*---------------------------------------------------------------------------*/