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 */
363 assert(i == set_v[set].count);
374 const struct set *curr_set(void)
379 const struct level *get_level(int i)
381 return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
384 /*---------------------------------------------------------------------------*/
386 /* Update the level score rank according to coins and timer. */
387 static int level_score_update(struct level_game *lg, const char *player)
389 int timer = lg->timer;
390 int coins = lg->coins;
391 struct level *l = &level_v[lg->level->number];
393 lg->time_rank = score_time_insert(&l->score.best_times,
394 player, timer, coins);
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);
402 lg->coin_rank = score_coin_insert(&l->score.most_coins,
403 player, timer, coins);
405 return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
408 /* Update the set score rank according to score and times. */
409 static int set_score_update(struct level_game *lg, const char *player)
411 int timer = lg->times;
412 int coins = lg->score;
413 struct set *s = &set_v[set];
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);
418 return (lg->score_rank < 3 || lg->times_rank < 3);
421 /* Update the player name for set and level high-score. */
422 void score_change_name(struct level_game *lg, const char *player)
424 struct set *s = &set_v[set];
425 struct level *l = &level_v[lg->level->number];
427 strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
428 strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
429 strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
431 strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
432 strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
437 static struct level *next_level(int i)
439 return set_level_exists(&set_v[set], i + 1) ? &level_v[i + 1] : NULL;
442 static struct level *next_normal_level(int i)
444 for (i++; i < set_v[set].count; i++)
445 if (!level_v[i].is_bonus)
451 /*---------------------------------------------------------------------------*/
453 void set_finish_level(struct level_game *lg, const char *player)
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? */
461 assert(s == cl->set);
463 /* if no set, no next level */
466 /* if no set, return */
467 lg->next_level = NULL;
471 /* On level completed */
472 if (lg->status == GAME_GOAL)
474 /* Update level scores */
475 dirty = level_score_update(lg, player);
477 /* Complete the level */
478 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
480 /* Complete the level */
481 if (!cl->is_completed)
483 cl->is_completed = 1;
490 /* On goal reached */
491 if (lg->status == GAME_GOAL)
493 /* Identify the following level */
499 /* Skip bonuses if unlocked in any mode */
503 if (lg->mode == MODE_CHALLENGE && nl->is_locked)
508 lg->bonus_repr = nl->repr;
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 /*---------------------------------------------------------------------------*/