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