2 * Copyright (C) 2003 Robert Kooima
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.
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.
26 /*---------------------------------------------------------------------------*/
28 static int count; /* number of sets */
30 static struct set set_v[MAXSET]; /* array of sets */
32 static struct set *current_set; /* currently selected set */
34 static struct level level_v[MAXLVL]; /* levels of the current set */
36 /*---------------------------------------------------------------------------*/
38 static void put_score(FILE *fp, const struct score *s)
41 for (j = 0; j < NSCORE; j++)
42 fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
45 static void set_store_hs(void)
46 /* Store the score of the set */
48 const struct set *s = current_set;
51 const struct level *l;
52 char states[MAXLVL + 1];
54 if ((fout = fopen(config_user(s->user_scores), "w")))
56 for (i = 0; i < s->count; i++)
58 if (level_v[i].is_locked)
60 else if (level_v[i].is_completed)
65 states[s->count] = '\0';
66 fprintf(fout, "%s\n",states);
68 put_score(fout, &s->time_score);
69 put_score(fout, &s->coin_score);
71 for (i = 0; i < s->count; i++)
74 put_score(fout, &l->score.best_times);
75 put_score(fout, &l->score.unlock_goal);
76 put_score(fout, &l->score.most_coins);
83 static int get_score(FILE *fp, struct score *s)
87 for (j = 0; j < NSCORE && res; j++)
89 res = (fscanf(fp, "%d %d %s\n",
90 &s->timer[j], &s->coins[j], s->player[j])) == 3;
95 static void set_load_hs(void)
96 /* Get the score of the set */
98 struct set *s = current_set;
103 const char *fn = config_user(s->user_scores);
104 char states[MAXLVL + 1];
106 if ((fin = fopen(fn, "r")))
108 res = ((fscanf(fin, "%s\n", states) == 1) &&
109 (strlen(states) == s->count));
110 for (i = 0; i < s->count && res; i++)
115 level_v[i].is_locked = 1;
116 level_v[i].is_completed = 0;
120 level_v[i].is_locked = 0;
121 level_v[i].is_completed = 1;
125 level_v[i].is_locked = 0;
126 level_v[i].is_completed = 0;
135 get_score(fin, &s->time_score) &&
136 get_score(fin, &s->coin_score);
138 for (i = 0; i < s->count && res; i++)
141 res = get_score(fin, &l->score.best_times) &&
142 get_score(fin, &l->score.unlock_goal) &&
143 get_score(fin, &l->score.most_coins);
149 if (!res && errno != ENOENT)
152 _("Error while loading user high-score file '%s': %s\n"),
153 fn, errno ? strerror(errno) : _("Incorrect format"));
157 static char *strip_eol(char *str)
159 char *c = str + strlen(str) - 1;
161 while (c >= str && (*c == '\n' || *c =='\r'))
167 static int set_load(struct set *s, const char *filename)
173 fin = fopen(config_data(filename), "r");
177 fprintf(stderr, _("Cannot load the set file '%s': %s\n"),
178 filename, strerror(errno));
182 memset(s, 0, sizeof (struct set));
184 /* Set some sane values in case the scores hs is missing. */
186 score_init_hs(&s->time_score, 359999, 0);
187 score_init_hs(&s->coin_score, 359999, 0);
189 /* Load set metadata. */
191 strcpy(s->file, filename);
193 if ((res = fgets(buf, MAXSTR, fin) != NULL))
194 strcpy(s->name, strip_eol(buf));
195 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
196 strcpy(s->desc, strip_eol(buf));
197 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
198 strcpy(s->setname, strip_eol(buf));
199 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
200 strcpy(s->shot, strip_eol(buf));
201 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
202 sscanf(buf, "%d %d %d %d %d %d",
203 &s->time_score.timer[0],
204 &s->time_score.timer[1],
205 &s->time_score.timer[2],
206 &s->coin_score.coins[0],
207 &s->coin_score.coins[1],
208 &s->coin_score.coins[2]);
210 strcpy(s->user_scores, "neverballhs-");
211 strcat(s->user_scores, s->setname);
217 while (s->count < MAXLVL && (fscanf(fin, "%s", buf) == 1))
222 /* Load the levels states (stored in the user high score file) */
224 s->locked = s->count;
227 if ((fin = fopen(config_user(s->user_scores), "r")))
229 char states[MAXLVL + 1];
231 if ((fscanf(fin, "%s\n", states) == 1) && (strlen(states) == s->count))
233 for (i = 0; i < s->count; i++)
235 if (states[i] == 'O')
237 else if (states[i] == 'C')
246 if (s->locked == s->count)
247 s->locked = s->count-1;
252 /*---------------------------------------------------------------------------*/
262 if ((fin = fopen(config_data(SET_FILE), "r")))
264 while (count < MAXSET && fgets(name, MAXSTR, fin))
265 if (set_load(&set_v[count], strip_eol(name)))
274 /*---------------------------------------------------------------------------*/
276 int set_exists(int i)
278 return (0 <= i && i < count);
281 const struct set *get_set(int i)
283 return set_exists(i) ? &set_v[i] : NULL;
286 /*---------------------------------------------------------------------------*/
288 int set_unlocked(const struct set *s)
289 /* Are all levels (even extra bonus) unlocked? */
291 return s->locked == 0;
294 int set_completed(const struct set *s)
295 /* Are all levels (even extra bonus) completed? */
297 return s->completed == s->count;
300 int set_level_exists(const struct set *s, int i)
301 /* Does the level i of the set exist? */
303 return (i >= 0) && (i < s->count);
306 /*---------------------------------------------------------------------------*/
308 static void set_load_levels(void)
319 const char *roman[] = {
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"
328 if ((fin = fopen(config_data(current_set->file), "r")))
332 /* Skip the five first lines */
333 for (i = 0; i < 5; i++)
334 fgets(buf, MAXSTR, fin);
336 for (i = 0; i < current_set->count && res; i++)
340 res = (fscanf(fin, "%s", name) == 1);
345 /* Initialize set related info */
346 l->set = current_set;
350 sprintf(l->repr, "%s", roman[bnb++]);
352 sprintf(l->repr, "%02d", nb++);
357 level_v[0].is_locked = 0; /* unlock the first level */
361 assert(i == current_set->count);
366 current_set = &set_v[i];
371 const struct set *curr_set(void)
376 const struct level *get_level(int i)
378 return (i >= 0 && i < current_set->count) ? &level_v[i] : NULL;
381 /*---------------------------------------------------------------------------*/
383 static int score_time_comp(const struct score *S, int i, int j)
385 if (S->timer[i] < S->timer[j])
388 if (S->timer[i] == S->timer[j] && S->coins[i] > S->coins[j])
394 static int score_coin_comp(const struct score *S, int i, int j)
396 if (S->coins[i] > S->coins[j])
399 if (S->coins[i] == S->coins[j] && S->timer[i] < S->timer[j])
405 static void score_swap(struct score *S, int i, int j)
410 strncpy(player, S->player[i], MAXNAM);
411 strncpy(S->player[i], S->player[j], MAXNAM);
412 strncpy(S->player[j], player, MAXNAM);
415 S->timer[i] = S->timer[j];
419 S->coins[i] = S->coins[j];
423 static int score_time_insert(struct score *s, const char *player, int timer,
428 strncpy(s->player[3], player, MAXNAM);
432 for (i = 2; i >= 0 && score_time_comp(s, i + 1, i); i--)
433 score_swap(s, i + 1, i);
438 static int score_coin_insert(struct score *s, const char *player, int timer,
443 strncpy(s->player[3], player, MAXNAM);
447 for (i = 2; i >= 0 && score_coin_comp(s, i + 1, i); i--)
448 score_swap(s, i + 1, i);
453 static int level_score_update(struct level_game *lg, const char *player)
454 /* Update the level score rank according to coins and timer */
456 int timer = lg->timer;
457 int coins = lg->coins;
458 struct level *l = &level_v[lg->level->number];
460 lg->time_rank = score_time_insert(&l->score.best_times,
461 player, timer, coins);
463 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
464 lg->goal_rank = score_time_insert(&l->score.unlock_goal,
465 player, timer, coins);
469 lg->coin_rank = score_coin_insert(&l->score.most_coins,
470 player, timer, coins);
472 return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
475 static int set_score_update(struct level_game *lg, const char *player)
476 /* Update the set score rank according to score and times */
478 int timer = lg->times;
479 int coins = lg->score;
480 struct set *s = current_set;
482 lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
483 lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
484 return (lg->score_rank < 3 || lg->times_rank < 3);
488 void score_change_name(struct level_game *lg, const char *player)
489 /* Update the player name for set and level high-score */
491 #define UPDATE(i, x) (strncpy((x).player[(i)], player, MAXNAM))
492 struct set *s = current_set;
493 struct level *l = &level_v[lg->level->number];
494 UPDATE(lg->time_rank, l->score.best_times);
495 UPDATE(lg->goal_rank, l->score.unlock_goal);
496 UPDATE(lg->coin_rank, l->score.most_coins);
497 UPDATE(lg->score_rank, s->coin_score);
498 UPDATE(lg->times_rank, s->time_score);
502 static struct level *next_level(int i)
504 /* Return the ith level, or NULL */
505 return set_level_exists(current_set, i + 1) ? &level_v[i + 1] : NULL;
508 static struct level *next_normal_level(int i)
509 /* Return the next normal level (starting for i)
510 * Return NULL if there is not a such level */
512 for (i++; i < current_set->count; i++)
513 if (!level_v[i].is_bonus)
518 void set_finish_level(struct level_game *lg, const char *player)
519 /* Inform the set that a level is finished.
520 * Update next_level and score rank fields */
522 struct set *s = current_set;
523 int ln = lg->level->number; /* current level number */
524 struct level *cl = &level_v[ln]; /* current level */
525 struct level *nl = NULL; /* next level */
526 int dirty = 0; /* HS should be saved? */
528 assert(s == cl->set);
530 /* if no set, no next level */
533 /* if no set, return */
534 lg->next_level = NULL;
538 /* On level completed */
539 if (lg->state == GAME_GOAL)
541 /* Update level scores */
542 dirty = level_score_update(lg, player);
544 /* Complete the level */
545 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
547 /* Complete the level */
548 if (!cl->is_completed)
550 cl->is_completed = 1;
557 /* On goal reached */
558 if (lg->state == GAME_GOAL || lg->state == GAME_SPEC)
560 /* Identify the following level */
561 nl = next_level(ln + lg->state_value);
564 /* skip bonuses if unlocked in non challenge mode */
565 if (nl->is_bonus && nl->is_locked && lg->mode != MODE_CHALLENGE)
566 nl = next_normal_level(nl->number);
568 else if (lg->mode == MODE_CHALLENGE)
571 else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
573 /* On fail, identify the next level (only in bonus for challenge) */
574 nl = next_normal_level(ln);
575 /* Next level may be unavailable */
576 if (!cl->is_bonus && nl != NULL && nl->is_locked)
578 /* Fail a bonus level but win the set! */
579 else if (nl == NULL && lg->mode == MODE_CHALLENGE)
586 /* update set score */
587 set_score_update(lg, player);
588 /* unlock all levels */
593 /* unlock the next level if needed */
594 if (nl != NULL && nl->is_locked)
596 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
607 /* got the next level */
615 /*---------------------------------------------------------------------------*/
617 void level_snap(int i)
619 char filename[MAXSTR];
622 /* Convert the level name to a PNG filename. */
624 memset(filename, 0, MAXSTR);
626 ext = strrchr(level_v[i].file, '.');
627 strncpy(filename, level_v[i].file,
628 ext ? ext - level_v[i].file : strlen(level_v[i].file));
629 strcat(filename, ".png");
631 /* Initialize the game for a snapshot. */
633 if (game_init(&level_v[i], 0, 0))
637 if ((shadow = config_get_d(CONFIG_SHADOW)))
638 config_set_d(CONFIG_SHADOW, 0);
640 /* Render the level and grab the screen. */
646 SDL_GL_SwapBuffers();
648 image_snap(filename);
651 config_set_d(CONFIG_SHADOW, 1);
656 /* Open each level of the current set */
659 current_set->locked = 0;
660 for (i = 0; i < current_set->count; i++)
661 level_v[i].is_locked = 0;
665 /*---------------------------------------------------------------------------*/