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 /*---------------------------------------------------------------------------*/
29 static struct set set_v[MAXSET];
31 static struct set *current_set;
32 static struct level level_v[MAXLVL];
34 /*---------------------------------------------------------------------------*/
36 static void put_score(FILE *fp, const struct score *s)
40 for (j = 0; j < NSCORE; j++)
41 fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
44 /* Store the score of the set. */
45 static void set_store_hs(void)
47 const struct set *s = current_set;
50 const struct level *l;
51 char states[MAXLVL + 1];
53 if ((fout = fopen(config_user(s->user_scores), "w")))
55 for (i = 0; i < s->count; i++)
57 if (level_v[i].is_locked)
59 else if (level_v[i].is_completed)
64 states[s->count] = '\0';
65 fprintf(fout, "%s\n",states);
67 put_score(fout, &s->time_score);
68 put_score(fout, &s->coin_score);
70 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)
88 for (j = 0; j < NSCORE && res; j++)
90 res = fscanf(fp, "%d %d %s\n",
98 /* Get the score of the set. */
99 static void set_load_hs(void)
101 struct set *s = current_set;
106 const char *fn = config_user(s->user_scores);
107 char states[MAXLVL + 1];
109 if ((fin = fopen(fn, "r")))
111 res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
113 for (i = 0; i < s->count && res; i++)
118 level_v[i].is_locked = 1;
119 level_v[i].is_completed = 0;
123 level_v[i].is_locked = 0;
124 level_v[i].is_completed = 1;
128 level_v[i].is_locked = 0;
129 level_v[i].is_completed = 0;
138 get_score(fin, &s->time_score) &&
139 get_score(fin, &s->coin_score);
141 for (i = 0; i < s->count && res; i++)
144 res = get_score(fin, &l->score.best_times) &&
145 get_score(fin, &l->score.unlock_goal) &&
146 get_score(fin, &l->score.most_coins);
152 if (!res && errno != ENOENT)
155 _("Error while loading user high-score file '%s': %s\n"),
156 fn, errno ? strerror(errno) : _("Incorrect format"));
160 static char *strip_eol(char *str)
162 char *c = str + strlen(str) - 1;
164 while (c >= str && (*c == '\n' || *c =='\r'))
170 static int set_load(struct set *s, const char *filename)
176 fin = fopen(config_data(filename), "r");
180 fprintf(stderr, _("Cannot load the set file '%s': %s\n"),
181 filename, strerror(errno));
185 memset(s, 0, sizeof (struct set));
187 /* Set some sane values in case the scores hs is missing. */
189 score_init_hs(&s->time_score, 359999, 0);
190 score_init_hs(&s->coin_score, 359999, 0);
192 /* Load set metadata. */
194 strcpy(s->file, filename);
196 if ((res = fgets(buf, MAXSTR, fin) != NULL))
197 strcpy(s->name, strip_eol(buf));
198 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
199 strcpy(s->desc, strip_eol(buf));
200 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
201 strcpy(s->id, strip_eol(buf));
202 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
203 strcpy(s->shot, strip_eol(buf));
204 if (res && (res = fgets(buf, MAXSTR, fin) != NULL))
205 sscanf(buf, "%d %d %d %d %d %d",
206 &s->time_score.timer[0],
207 &s->time_score.timer[1],
208 &s->time_score.timer[2],
209 &s->coin_score.coins[0],
210 &s->coin_score.coins[1],
211 &s->coin_score.coins[2]);
213 strcpy(s->user_scores, "neverballhs-");
214 strcat(s->user_scores, s->id);
220 while (s->count < MAXLVL && (fscanf(fin, "%s", buf) == 1))
225 /* Load the levels states (stored in the user high score file) */
227 s->locked = s->count;
230 if ((fin = fopen(config_user(s->user_scores), "r")))
232 char states[MAXLVL + 1];
234 if ((fscanf(fin, "%s\n", states) == 1) && (strlen(states) == s->count))
236 for (i = 0; i < s->count; i++)
238 if (states[i] == 'O')
240 else if (states[i] == 'C')
249 if (s->locked == s->count)
250 s->locked = s->count-1;
255 /*---------------------------------------------------------------------------*/
265 if ((fin = fopen(config_data(SET_FILE), "r")))
267 while (set_count < MAXSET && fgets(name, MAXSTR, fin))
268 if (set_load(&set_v[set_count], strip_eol(name)))
277 /*---------------------------------------------------------------------------*/
279 int set_exists(int i)
281 return (0 <= i && i < set_count);
284 const struct set *get_set(int i)
286 return set_exists(i) ? &set_v[i] : NULL;
289 /*---------------------------------------------------------------------------*/
291 int set_unlocked(const struct set *s)
292 /* Are all levels (even extra bonus) unlocked? */
294 return s->locked == 0;
297 int set_completed(const struct set *s)
298 /* Are all levels (even extra bonus) completed? */
300 return s->completed == s->count;
303 int set_level_exists(const struct set *s, int i)
304 /* Does the level i of the set exist? */
306 return (i >= 0) && (i < s->count);
309 /*---------------------------------------------------------------------------*/
311 static void set_load_levels(void)
322 const char *roman[] = {
324 "I", "II", "III", "IV", "V",
325 "VI", "VII", "VIII", "IX", "X",
326 "XI", "XII", "XIII", "XIV", "XV",
327 "XVI", "XVII", "XVIII", "XIX", "XX",
328 "XXI", "XXII", "XXIII", "XXIV", "XXV"
331 if ((fin = fopen(config_data(current_set->file), "r")))
335 /* Skip the five first lines */
336 for (i = 0; i < 5; i++)
337 fgets(buf, MAXSTR, fin);
339 for (i = 0; i < current_set->count && res; i++)
343 res = (fscanf(fin, "%s", name) == 1);
348 /* Initialize set related info */
349 l->set = current_set;
353 sprintf(l->repr, "%s", roman[bnb++]);
355 sprintf(l->repr, "%02d", nb++);
360 level_v[0].is_locked = 0; /* unlock the first level */
364 assert(i == current_set->count);
369 current_set = &set_v[i];
374 const struct set *curr_set(void)
379 const struct level *get_level(int i)
381 return (i >= 0 && i < current_set->count) ? &level_v[i] : NULL;
384 /*---------------------------------------------------------------------------*/
386 static int level_score_update(struct level_game *lg, const char *player)
387 /* Update the level score rank according to coins and timer */
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 static int set_score_update(struct level_game *lg, const char *player)
409 /* Update the set score rank according to score and times */
411 int timer = lg->times;
412 int coins = lg->score;
413 struct set *s = current_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);
417 return (lg->score_rank < 3 || lg->times_rank < 3);
421 void score_change_name(struct level_game *lg, const char *player)
422 /* Update the player name for set and level high-score */
424 #define UPDATE(i, x) (strncpy((x).player[(i)], player, MAXNAM))
425 struct set *s = current_set;
426 struct level *l = &level_v[lg->level->number];
427 UPDATE(lg->time_rank, l->score.best_times);
428 UPDATE(lg->goal_rank, l->score.unlock_goal);
429 UPDATE(lg->coin_rank, l->score.most_coins);
430 UPDATE(lg->score_rank, s->coin_score);
431 UPDATE(lg->times_rank, s->time_score);
435 static struct level *next_level(int i)
437 /* Return the ith level, or NULL */
438 return set_level_exists(current_set, i + 1) ? &level_v[i + 1] : NULL;
441 static struct level *next_normal_level(int i)
442 /* Return the next normal level (starting for i)
443 * Return NULL if there is not a such level */
445 for (i++; i < current_set->count; i++)
446 if (!level_v[i].is_bonus)
451 void set_finish_level(struct level_game *lg, const char *player)
452 /* Inform the set that a level is finished.
453 * Update next_level and score rank fields */
455 struct set *s = current_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; /* HS should 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->state == 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->state == GAME_GOAL || lg->state == GAME_SPEC)
493 /* Identify the following level */
494 nl = next_level(ln + lg->state_value);
497 /* skip bonuses if unlocked in non challenge mode */
498 if (nl->is_bonus && nl->is_locked && lg->mode != MODE_CHALLENGE)
499 nl = next_normal_level(nl->number);
501 else if (lg->mode == MODE_CHALLENGE)
504 else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
506 /* On fail, identify the next level (only in bonus for challenge) */
507 nl = next_normal_level(ln);
508 /* Next level may be unavailable */
509 if (!cl->is_bonus && nl != NULL && nl->is_locked)
511 /* Fail a bonus level but win the set! */
512 else if (nl == NULL && lg->mode == MODE_CHALLENGE)
519 /* update set score */
520 set_score_update(lg, player);
521 /* unlock all levels */
526 /* unlock the next level if needed */
527 if (nl != NULL && nl->is_locked)
529 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
540 /* got the next level */
548 /*---------------------------------------------------------------------------*/
550 void level_snap(int i)
552 char filename[MAXSTR];
555 /* Convert the level name to a PNG filename. */
557 memset(filename, 0, MAXSTR);
559 ext = strrchr(level_v[i].file, '.');
560 strncpy(filename, level_v[i].file,
561 ext ? ext - level_v[i].file : strlen(level_v[i].file));
562 strcat(filename, ".png");
564 /* Initialize the game for a snapshot. */
566 if (game_init(&level_v[i], 0, 0))
570 if ((shadow = config_get_d(CONFIG_SHADOW)))
571 config_set_d(CONFIG_SHADOW, 0);
573 /* Render the level and grab the screen. */
579 SDL_GL_SwapBuffers();
581 image_snap(filename);
584 config_set_d(CONFIG_SHADOW, 1);
589 /* Open each level of the current set */
592 current_set->locked = 0;
593 for (i = 0; i < current_set->count; i++)
594 level_v[i].is_locked = 0;
598 /*---------------------------------------------------------------------------*/