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.
27 /*---------------------------------------------------------------------------*/
32 static struct set set_v[MAXSET];
33 static struct level level_v[MAXLVL];
35 /*---------------------------------------------------------------------------*/
37 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 /* Store the score of the set. */
46 static void set_store_hs(void)
48 const struct set *s = &set_v[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++)
75 put_score(fout, &l->score.best_times);
76 put_score(fout, &l->score.unlock_goal);
77 put_score(fout, &l->score.most_coins);
84 static int get_score(FILE *fp, struct score *s)
89 for (j = 0; j < NSCORE && res; j++)
91 res = fscanf(fp, "%d %d %s\n",
99 /* Get the score of the set. */
100 static void set_load_hs(void)
102 struct set *s = &set_v[set];
107 const char *fn = config_user(s->user_scores);
108 char states[MAXLVL + 1];
110 if ((fin = fopen(fn, "r")))
112 res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
114 for (i = 0; i < s->count && res; i++)
119 level_v[i].is_locked = 1;
120 level_v[i].is_completed = 0;
124 level_v[i].is_locked = 0;
125 level_v[i].is_completed = 1;
129 level_v[i].is_locked = 0;
130 level_v[i].is_completed = 0;
139 get_score(fin, &s->time_score) &&
140 get_score(fin, &s->coin_score);
142 for (i = 0; i < s->count && res; i++)
145 res = get_score(fin, &l->score.best_times) &&
146 get_score(fin, &l->score.unlock_goal) &&
147 get_score(fin, &l->score.most_coins);
153 if (!res && errno != ENOENT)
156 L_("Error while loading user high-score file '%s': %s\n"),
157 fn, errno ? strerror(errno) : L_("Incorrect format"));
161 static char *strip_eol(char *str)
163 char *c = str + strlen(str) - 1;
165 while (c >= str && (*c == '\n' || *c =='\r'))
171 static int set_load(struct set *s, const char *filename)
177 fin = fopen(config_data(filename), "r");
181 fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
182 filename, strerror(errno));
186 memset(s, 0, sizeof (struct set));
188 /* Set some sane values in case the scores hs is missing. */
190 score_init_hs(&s->time_score, 359999, 0);
191 score_init_hs(&s->coin_score, 359999, 0);
193 /* Load set metadata. */
195 strcpy(s->file, filename);
197 if ((res = fgets(buf, MAXSTR, fin) != NULL))
198 strcpy(s->name, strip_eol(buf));
199 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
200 strcpy(s->desc, strip_eol(buf));
201 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
202 strcpy(s->id, strip_eol(buf));
203 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
204 strcpy(s->shot, strip_eol(buf));
205 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
206 sscanf(buf, "%d %d %d %d %d %d",
207 &s->time_score.timer[0],
208 &s->time_score.timer[1],
209 &s->time_score.timer[2],
210 &s->coin_score.coins[0],
211 &s->coin_score.coins[1],
212 &s->coin_score.coins[2]);
214 strcpy(s->user_scores, "neverballhs-");
215 strcat(s->user_scores, s->id);
221 while (s->count < MAXLVL && (fscanf(fin, "%s", buf) == 1))
226 /* Load the levels states (stored in the user high score file) */
228 s->locked = s->count;
231 if ((fin = fopen(config_user(s->user_scores), "r")))
233 char states[MAXLVL + 1];
235 if ((fscanf(fin, "%s\n", states) == 1) && (strlen(states) == s->count))
237 for (i = 0; i < s->count; i++)
239 if (states[i] == 'O')
241 else if (states[i] == 'C')
250 if (s->locked == s->count)
251 s->locked = s->count-1;
256 /*---------------------------------------------------------------------------*/
266 if ((fin = fopen(config_data(SET_FILE), "r")))
268 while (count < MAXSET && fgets(name, MAXSTR, fin))
269 if (set_load(&set_v[count], strip_eol(name)))
278 /*---------------------------------------------------------------------------*/
280 int set_exists(int i)
282 return (0 <= i && i < count);
285 const struct set *get_set(int i)
287 return set_exists(i) ? &set_v[i] : NULL;
290 /*---------------------------------------------------------------------------*/
292 int set_unlocked(const struct set *s)
294 return s->locked == 0;
297 int set_completed(const struct set *s)
299 return s->completed == s->count;
302 int set_level_exists(const struct set *s, int i)
304 return (i >= 0) && (i < s->count);
307 /*---------------------------------------------------------------------------*/
309 static void set_load_levels(void)
321 const char *roman[] = {
323 "I", "II", "III", "IV", "V",
324 "VI", "VII", "VIII", "IX", "X",
325 "XI", "XII", "XIII", "XIV", "XV",
326 "XVI", "XVII", "XVIII", "XIX", "XX",
327 "XXI", "XXII", "XXIII", "XXIV", "XXV"
330 if ((fin = fopen(config_data(set_v[set].file), "r")))
334 /* Skip the five first lines */
335 for (i = 0; i < 5; i++)
336 fgets(buf, MAXSTR, fin);
338 for (i = 0; i < set_v[set].count && res; i++)
342 res = (fscanf(fin, "%s", name) == 1);
347 /* Initialize set related info */
348 l->set = &set_v[set];
352 sprintf(l->repr, "%s", roman[bnb++]);
354 sprintf(l->repr, "%02d", nb++);
359 level_v[0].is_locked = 0; /* unlock the first level */
365 assert(i == set_v[set].count);
376 const struct set *curr_set(void)
381 const struct level *get_level(int i)
383 return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
386 /*---------------------------------------------------------------------------*/
388 /* Update the level score rank according to coins and timer. */
389 static int level_score_update(struct level_game *lg, const char *player)
391 int timer = lg->timer;
392 int coins = lg->coins;
393 struct level *l = &level_v[lg->level->number];
395 lg->time_rank = score_time_insert(&l->score.best_times,
396 player, timer, coins);
398 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
399 lg->goal_rank = score_time_insert(&l->score.unlock_goal,
400 player, timer, coins);
404 lg->coin_rank = score_coin_insert(&l->score.most_coins,
405 player, timer, coins);
407 return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
410 /* Update the set score rank according to score and times. */
411 static int set_score_update(struct level_game *lg, const char *player)
413 int timer = lg->times;
414 int coins = lg->score;
415 struct set *s = &set_v[set];
417 lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
418 lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
420 return (lg->score_rank < 3 || lg->times_rank < 3);
423 /* Update the player name for set and level high-score. */
424 void score_change_name(struct level_game *lg, const char *player)
426 struct set *s = &set_v[set];
427 struct level *l = &level_v[lg->level->number];
429 strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
430 strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
431 strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
433 strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
434 strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
439 static struct level *next_level(int i)
441 return set_level_exists(&set_v[set], i + 1) ? &level_v[i + 1] : NULL;
444 static struct level *next_normal_level(int i)
446 for (i++; i < set_v[set].count; i++)
447 if (!level_v[i].is_bonus)
453 /*---------------------------------------------------------------------------*/
455 void set_finish_level(struct level_game *lg, const char *player)
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? */
463 assert(s == cl->set);
465 /* if no set, no next level */
468 /* if no set, return */
469 lg->next_level = NULL;
473 /* On level completed */
474 if (lg->status == GAME_GOAL)
476 /* Update level scores */
477 dirty = level_score_update(lg, player);
479 /* Complete the level */
480 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
482 /* Complete the level */
483 if (!cl->is_completed)
485 cl->is_completed = 1;
492 /* On goal reached */
493 if (lg->status == GAME_GOAL)
495 /* Identify the following level */
501 /* Skip bonuses if unlocked in any mode */
505 if (lg->mode == MODE_CHALLENGE && nl->is_locked)
511 nl = next_normal_level(nl->number);
513 if (nl == NULL && lg->mode == MODE_CHALLENGE)
519 else if (lg->mode == MODE_CHALLENGE)
522 else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
524 /* On fail, identify the next level (only in bonus for challenge) */
525 nl = next_normal_level(ln);
526 /* Next level may be unavailable */
527 if (!cl->is_bonus && nl != NULL && nl->is_locked)
529 /* Fail a bonus level but win the set! */
530 else if (nl == NULL && lg->mode == MODE_CHALLENGE)
537 /* update set score */
538 set_score_update(lg, player);
539 /* unlock all levels */
544 /* unlock the next level if needed */
545 if (nl != NULL && nl->is_locked)
547 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
558 /* got the next level */
566 /*---------------------------------------------------------------------------*/
568 void level_snap(int i)
570 char filename[MAXSTR];
573 /* Convert the level name to a PNG filename. */
575 memset(filename, 0, MAXSTR);
577 ext = strrchr(level_v[i].file, '.');
578 strncpy(filename, level_v[i].file,
579 ext ? ext - level_v[i].file : strlen(level_v[i].file));
580 strcat(filename, ".png");
582 /* Initialize the game for a snapshot. */
584 if (game_init(&level_v[i], 0, 0))
586 /* Render the level and grab the screen. */
592 SDL_GL_SwapBuffers();
594 image_snap(filename);
599 /* Open each level of the current set */
603 set_v[set].locked = 0;
605 for (i = 0; i < set_v[set].count; i++)
606 level_v[i].is_locked = 0;
610 /*---------------------------------------------------------------------------*/