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;
57 if ((fout = fopen(config_user(s->user_scores), "w")))
59 fprintf(fout, "%d\n", s->limit);
61 put_score(fout, &s->time_score);
62 put_score(fout, &s->coin_score);
64 for (i = 0 ; i <= lim ; i++)
67 put_score(fout, &l->time_score);
68 put_score(fout, &l->goal_score);
69 put_score(fout, &l->coin_score);
76 static int get_score(FILE *fp, struct score *s)
80 for (j = 0; j < NSCORE && res; j++)
82 res = (fscanf(fp, "%d %d %s\n",
83 &s->timer[j], &s->coins[j], s->player[j])) == 3;
88 static void set_load_hs(void)
89 /* Get the score of the set */
91 struct set *s = current_set;
96 const char *fn = config_user(s->user_scores);
99 if ((fin = fopen(fn, "r")))
101 res = (fscanf(fin, "%d\n", &lim) == 1) &&
103 get_score(fin, &s->time_score) &&
104 get_score(fin, &s->coin_score);
109 for (i = 0; i <= lim && res; i++)
112 res = get_score(fin, &l->time_score) &&
113 get_score(fin, &l->goal_score) &&
114 get_score(fin, &l->coin_score);
120 if (!res && errno != ENOENT)
122 fprintf(stderr, _("Error while loading user high-score file '%s': "), fn);
126 fprintf(stderr, _("Incorrect format\n"));
130 static const char * numbernames[] = {
131 "01", "02", "03", "04", "05",
132 "06", "07", "08", "09", "10",
133 "11", "12", "13", "14", "15",
134 "16", "17", "18", "19", "20",
135 N_("B1"), N_("B2"), N_("B3"), N_("B4"), N_("B5")};
138 static char* chomp(char *str)
139 /* Remove trailing \n if any */
141 char *p = str + strlen(str) - 1;
142 if (p >= str && *p == '\n') *p = 0;
146 static int set_load(struct set *s, const char *filename)
153 /* Open the datafile */
155 fin = fopen(filename, "r");
158 fprintf(stderr, _("Cannot load the set file '%s':"), filename);
163 /* Raz the set structure */
165 memset(s, 0, sizeof(struct set));
167 /* Set some sane values in case the scores hs is missing. */
169 score_init_hs(&s->time_score, 359999, 0);
170 score_init_hs(&s->coin_score, 359999, 0);
172 /* Load set metadata */
174 strcpy(s->file, filename);
175 if ((res = fgets(buf, MAXSTR, fin) != NULL))
176 strcpy(s->name, chomp(buf));
177 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
178 strcpy(s->desc, chomp(buf));
179 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
180 strcpy(s->setname, chomp(buf));
181 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
182 strcpy(s->shot, chomp(buf));
183 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
184 sscanf(buf, "%d %d %d %d %d %d",
185 &s->time_score.timer[0],
186 &s->time_score.timer[1],
187 &s->time_score.timer[2],
188 &s->coin_score.coins[0],
189 &s->coin_score.coins[1],
190 &s->coin_score.coins[2]);
191 strcpy(s->user_scores, "neverballhs-");
192 strcat(s->user_scores, s->setname);
194 /* Count levels levels. */
198 while (s->count < MAXLVL && fgets(buf, MAXSTR, fin))
201 /* Close the file, since it's no more needed */
205 /* Load the level limit (stored in the user highscore file) */
209 if ((fin = fopen(config_user(s->user_scores), "r")))
211 fscanf(fin, "%d\n", &s->limit);
212 if (s->limit > s->count)
220 /*---------------------------------------------------------------------------*/
226 char filename[MAXSTR];
233 if ((fin = fopen(config_data(SET_FILE), "r")))
236 while (count < MAXSET && res)
238 set = &(set_v[count]);
240 /* clean the set data */
242 res = (fgets(filename, MAXSTR, fin) != NULL);
247 res = set_load(set, config_data(filename));
260 /*---------------------------------------------------------------------------*/
262 int set_exists(int i)
264 return (0 <= i && i < count);
267 const struct set *get_set(int i)
269 return set_exists(i) ? &set_v[i] : NULL;
272 /*---------------------------------------------------------------------------*/
274 int set_extra_bonus_opened(const struct set *s)
275 /* Are extra bonus openned (ie challenge completed)? */
277 return s->limit >= 20;
280 int set_completed(const struct set *s)
281 /* Are all levels (even extra bonus) completed? */
283 return s->limit >= s->count;
286 int set_level_exists(const struct set *s, int i)
287 /* Is the level i of the set exists */
289 return (i >= 0) && (i < s->count);
292 /*---------------------------------------------------------------------------*/
294 static void set_load_levels(void)
295 /* Load more the levels of the current set */
304 fin = fopen(current_set->file, "r");
308 /* Skip the five first lines */
310 fgets(buf, MAXSTR, fin);
311 for(i=0; i<current_set->count && res; i++)
314 res = (fgets(buf, MAXSTR, fin) != NULL) &&
315 (sscanf(buf, "%s", name) == 1);
318 level_load(config_data(name), l);
320 /* Initialize set related info */
321 l->set = current_set;
323 l->numbername = numbernames[i];
324 l->is_locked = i > current_set->limit;
325 l->is_bonus = i >= 20;
328 assert(i == current_set->count);
333 assert(set_exists(i));
334 current_set = &set_v[i];
339 const struct set *curr_set(void)
344 const struct level *get_level(int i)
346 return (i>=0 && i<current_set->count) ? &level_v[i] : NULL;
349 /*---------------------------------------------------------------------------*/
351 static int score_time_comp(const struct score *S, int i, int j)
353 if (S->timer[i] < S->timer[j])
356 if (S->timer[i] == S->timer[j] &&
357 S->coins[i] > S->coins[j])
363 static int score_coin_comp(const struct score *S, int i, int j)
365 if (S->coins[i] > S->coins[j])
368 if (S->coins[i] == S->coins[j] &&
369 S->timer[i] < S->timer[j])
375 static void score_swap(struct score *S, int i, int j)
380 strncpy(player, S->player[i], MAXNAM);
381 strncpy(S->player[i], S->player[j], MAXNAM);
382 strncpy(S->player[j], player, MAXNAM);
385 S->timer[i] = S->timer[j];
389 S->coins[i] = S->coins[j];
393 static int score_time_insert(struct score *s, const char* player, int timer, int coins)
397 strncpy(s->player[3], player, MAXNAM);
401 for (i = 2; i >= 0 && score_time_comp(s, i + 1, i); i--)
402 score_swap(s, i + 1, i);
406 static int score_coin_insert(struct score *s, const char* player, int timer, int coins)
410 strncpy(s->player[3], player, MAXNAM);
414 for (i = 2; i >= 0 && score_coin_comp(s, i + 1, i); i--)
415 score_swap(s, i + 1, i);
419 static int level_score_update(struct level_game *lg, const char *player)
420 /* Update the level score rank according to coins and timer */
422 int timer = lg->timer;
423 int coins = lg->coins;
424 struct level * l = &level_v[lg->level->number];
426 lg->time_rank = score_time_insert(&l->time_score, player, timer, coins);
428 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
429 lg->goal_rank = score_time_insert(&l->goal_score, player, timer, coins);
433 lg->coin_rank = score_coin_insert(&l->coin_score, player, timer, coins);
435 return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
438 static int set_score_update(struct level_game *lg, const char *player)
439 /* Update the set score rank according to score and times */
441 int timer = lg->times;
442 int coins = lg->score;
443 struct set * s = current_set;
445 lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
446 lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
447 return (lg->score_rank < 3 || lg->times_rank < 3);
451 void score_change_name(struct level_game *lg, const char *player)
452 /* Update the player name for set and level high-score */
454 #define UPDATE(i, x) (strncpy((x).player[(i)], player, MAXNAM))
455 struct set * s = current_set;
456 struct level *l = &level_v[lg->level->number];
457 UPDATE(lg->time_rank, l->time_score);
458 UPDATE(lg->goal_rank, l->goal_score);
459 UPDATE(lg->coin_rank, l->coin_score);
460 UPDATE(lg->score_rank, s->coin_score);
461 UPDATE(lg->times_rank, s->time_score);
465 void set_finish_level(struct level_game *lg, const char *player)
466 /* Inform the set that a level is finished.
467 * Update next_level and score rank fields */
469 struct set *s = current_set;
470 int level = lg->level->number;
474 dirty = level_score_update(lg, player);
475 dirty = set_score_update(lg, player) || dirty;
477 /* compute the next level */
480 /* if no set, return */
481 lg->next_level = NULL;
485 level++; /* level is the next level */
487 /* if the next level is not oppened */
488 if (s->limit < level)
489 if ((lg->mode == MODE_CHALLENGE) ||
490 (lg->mode == MODE_NORMAL && (level < 20 || level > 20)))
492 level_v[level].is_locked = 0;
497 /* got the next level */
498 if (lg->mode == MODE_CHALLENGE && level >= 20)
499 lg->next_level = NULL; /* End the challenge */
500 else if (level < s->count && level <= s->limit)
501 lg->next_level = &level_v[level];
503 lg->next_level = NULL;
510 /*---------------------------------------------------------------------------*/
512 void level_snap(int i)
514 char filename[MAXSTR];
516 /* Convert the level name to a BMP filename. */
518 memset(filename, 0, MAXSTR);
519 strncpy(filename, level_v[i].file, strcspn(level_v[i].file, "."));
520 strcat(filename, ".bmp");
522 /* Initialize the game for a snapshot. */
524 if (game_init(&level_v[i], 0, 0))
526 /* Render the level and grab the screen. */
532 SDL_GL_SwapBuffers();
534 image_snap(filename, config_get_d(CONFIG_WIDTH), config_get_d(CONFIG_HEIGHT));
539 /* Open each level of the current set */
541 current_set->limit = current_set->count;
545 /*---------------------------------------------------------------------------*/