r1153@bld: parasti | 2007-09-22 04:40:05 +0300
[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
29 static struct set   set_v[MAXSET];
30 static int          set_count;
31 static struct set  *current_set;
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 = current_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 = current_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     current_set = NULL;
263     set_count   = 0;
264
265     if ((fin = fopen(config_data(SET_FILE), "r")))
266     {
267         while (set_count < MAXSET && fgets(name, MAXSTR, fin))
268             if (set_load(&set_v[set_count], strip_eol(name)))
269                 set_count++;
270
271         fclose(fin);
272     }
273
274     return set_count;
275 }
276
277 /*---------------------------------------------------------------------------*/
278
279 int  set_exists(int i)
280 {
281     return (0 <= i && i < set_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 /* Are all levels (even extra bonus) unlocked? */
293 {
294     return s->locked == 0;
295 }
296
297 int  set_completed(const struct set *s)
298 /* Are all levels (even extra bonus) completed? */
299 {
300     return s->completed == s->count;
301 }
302
303 int  set_level_exists(const struct set *s, int i)
304 /* Does the level i of the set exist? */
305 {
306     return (i >= 0) && (i < s->count);
307 }
308
309 /*---------------------------------------------------------------------------*/
310
311 static void set_load_levels(void)
312 {
313     FILE *fin;
314     struct level *l;
315
316     char buf[MAXSTR];
317     char name[MAXSTR];
318
319     int i = 0, res;
320     int nb = 1, bnb = 1;
321
322     const char *roman[] = {
323         NULL,
324         "I", "II", "III", "IV", "V",
325         "VI", "VII", "VIII", "IX", "X",
326         "XI", "XII", "XIII", "XIV", "XV",
327         "XVI", "XVII", "XVIII", "XIX", "XX",
328         "XXI", "XXII", "XXIII", "XXIV", "XXV"
329     };
330
331     if ((fin = fopen(config_data(current_set->file), "r")))
332     {
333         res = 1;
334
335         /* Skip the five first lines */
336         for (i = 0; i < 5; i++)
337             fgets(buf, MAXSTR, fin);
338
339         for (i = 0; i < current_set->count && res; i++)
340         {
341             l = &level_v[i];
342
343             res = (fscanf(fin, "%s", name) == 1);
344             assert(res);
345
346             level_load(name, l);
347
348             /* Initialize set related info */
349             l->set        = current_set;
350             l->number     = i;
351
352             if (l->is_bonus)
353                 sprintf(l->repr, "%s", roman[bnb++]);
354             else
355                 sprintf(l->repr, "%02d", nb++);
356
357             l->is_locked    = 1;
358             l->is_completed = 0;
359         }
360         level_v[0].is_locked = 0; /* unlock the first level */
361         fclose(fin);
362     }
363
364     assert(i == current_set->count);
365 }
366
367 void set_goto(int i)
368 {
369     current_set = &set_v[i];
370     set_load_levels();
371     set_load_hs();
372 }
373
374 const struct set *curr_set(void)
375 {
376     return current_set;
377 }
378
379 const struct level *get_level(int i)
380 {
381     return (i >= 0 && i < current_set->count) ? &level_v[i] : NULL;
382 }
383
384 /*---------------------------------------------------------------------------*/
385
386 static int level_score_update(struct level_game *lg, const char *player)
387 /* Update the level score rank according to coins and timer */
388 {
389     int timer = lg->timer;
390     int coins = lg->coins;
391     struct level *l = &level_v[lg->level->number];
392
393     lg->time_rank = score_time_insert(&l->score.best_times,
394                                       player, timer, coins);
395
396     if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
397         lg->goal_rank = score_time_insert(&l->score.unlock_goal,
398                                           player, timer, coins);
399     else
400         lg->goal_rank = 3;
401
402     lg->coin_rank = score_coin_insert(&l->score.most_coins,
403                                       player, timer, coins);
404
405     return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
406 }
407
408 static int set_score_update(struct level_game *lg, const char *player)
409 /* Update the set score rank according to score and times */
410 {
411     int timer = lg->times;
412     int coins = lg->score;
413     struct set *s = current_set;
414
415     lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
416     lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
417     return (lg->score_rank < 3 || lg->times_rank < 3);
418 }
419
420
421 void score_change_name(struct level_game *lg, const char *player)
422 /* Update the player name for set and level high-score */
423 {
424 #define UPDATE(i, x) (strncpy((x).player[(i)], player, MAXNAM))
425     struct set *s = current_set;
426     struct level *l = &level_v[lg->level->number];
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     set_store_hs();
433 }
434
435 static struct level *next_level(int i)
436 {
437 /* Return the ith level, or NULL */
438     return set_level_exists(current_set, i + 1) ? &level_v[i + 1] : NULL;
439 }
440
441 static struct level *next_normal_level(int i)
442 /* Return the next normal level (starting for i)
443  * Return NULL if there is not a such level */
444 {
445     for (i++; i < current_set->count; i++)
446         if (!level_v[i].is_bonus)
447             return &level_v[i];
448     return NULL;
449 }
450
451 void set_finish_level(struct level_game *lg, const char *player)
452 /* Inform the set that a level is finished.
453  * Update next_level and score rank fields */
454 {
455     struct set *s = current_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;              /* HS should 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->state == 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->state == GAME_GOAL || lg->state == GAME_SPEC)
492     {
493         /* Identify the following level */
494         nl = next_level(ln + lg->state_value);
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     current_set->locked = 0;
593     for (i = 0; i < current_set->count; i++)
594         level_v[i].is_locked = 0;
595 }
596
597
598 /*---------------------------------------------------------------------------*/