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)
320 const char *roman[] = {
322 "I", "II", "III", "IV", "V",
323 "VI", "VII", "VIII", "IX", "X",
324 "XI", "XII", "XIII", "XIV", "XV",
325 "XVI", "XVII", "XVIII", "XIX", "XX",
326 "XXI", "XXII", "XXIII", "XXIV", "XXV"
329 if ((fin = fopen(config_data(set_v[set].file), "r")))
333 /* Skip the five first lines */
334 for (i = 0; i < 5; i++)
335 fgets(buf, MAXSTR, fin);
337 for (i = 0; i < set_v[set].count && res; i++)
341 res = (fscanf(fin, "%s", name) == 1);
346 /* Initialize set related info */
347 l->set = &set_v[set];
351 sprintf(l->repr, "%s", roman[bnb++]);
353 sprintf(l->repr, "%02d", nb++);
358 level_v[0].is_locked = 0; /* unlock the first level */
362 assert(i == set_v[set].count);
364 struct level_game *lg = curr_lg();
365 lg->bonus = lg->bonusid = 0; /* initialize bonus values */
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 */
499 /* skip bonuses if unlocked in any mode */
502 if(lg->mode == MODE_CHALLENGE && nl->is_locked > 0)
504 lg->bonus = 1; /* Show GUI message */
505 nl->is_locked = 0; /* Unlock bonus level */
507 nl = next_normal_level(nl->number);
508 if(nl == NULL && lg->mode == MODE_CHALLENGE)
514 else if (lg->mode == MODE_CHALLENGE)
517 else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
519 /* On fail, identify the next level (only in bonus for challenge) */
520 nl = next_normal_level(ln);
521 /* Next level may be unavailable */
522 if (!cl->is_bonus && nl != NULL && nl->is_locked)
524 /* Fail a bonus level but win the set! */
525 else if (nl == NULL && lg->mode == MODE_CHALLENGE)
532 /* update set score */
533 set_score_update(lg, player);
534 /* unlock all levels */
539 /* unlock the next level if needed */
540 if (nl != NULL && nl->is_locked)
542 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
553 /* got the next level */
561 /*---------------------------------------------------------------------------*/
563 void level_snap(int i)
565 char filename[MAXSTR];
568 /* Convert the level name to a PNG filename. */
570 memset(filename, 0, MAXSTR);
572 ext = strrchr(level_v[i].file, '.');
573 strncpy(filename, level_v[i].file,
574 ext ? ext - level_v[i].file : strlen(level_v[i].file));
575 strcat(filename, ".png");
577 /* Initialize the game for a snapshot. */
579 if (game_init(&level_v[i], 0, 0))
581 /* Render the level and grab the screen. */
587 SDL_GL_SwapBuffers();
589 image_snap(filename);
594 /* Open each level of the current set */
598 set_v[set].locked = 0;
600 for (i = 0; i < set_v[set].count; i++)
601 level_v[i].is_locked = 0;
605 /*---------------------------------------------------------------------------*/