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.
28 /*---------------------------------------------------------------------------*/
34 char *id; /* Internal set identifier */
35 char *name; /* Set name */
36 char *desc; /* Set description */
37 char *shot; /* Set screen-shot */
39 char user_scores[PATHMAX]; /* User high-score file */
41 struct score time_score; /* Challenge score */
42 struct score coin_score; /* Challenge score */
46 int count; /* Number of levels */
47 char *level_name_v[MAXLVL]; /* List of level file names */
50 static int set_state = 0;
55 static struct set set_v[MAXSET];
56 static struct level level_v[MAXLVL];
58 /*---------------------------------------------------------------------------*/
60 static void put_score(FILE *fp, const struct score *s)
64 for (j = 0; j < NSCORE; j++)
65 fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
68 /* Store the score of the set. */
69 static void set_store_hs(void)
71 const struct set *s = &set_v[set];
74 const struct level *l;
75 char states[MAXLVL + 1];
77 if ((fout = fopen(config_user(s->user_scores), "w")))
79 for (i = 0; i < s->count; i++)
81 if (level_v[i].is_locked)
83 else if (level_v[i].is_completed)
88 states[s->count] = '\0';
89 fprintf(fout, "%s\n",states);
91 put_score(fout, &s->time_score);
92 put_score(fout, &s->coin_score);
94 for (i = 0; i < s->count; i++)
98 put_score(fout, &l->score.best_times);
99 put_score(fout, &l->score.unlock_goal);
100 put_score(fout, &l->score.most_coins);
107 static int get_score(FILE *fp, struct score *s)
112 for (j = 0; j < NSCORE && res; j++)
114 res = fscanf(fp, "%d %d %s\n",
122 /* Get the score of the set. */
123 static void set_load_hs(void)
125 struct set *s = &set_v[set];
130 const char *fn = config_user(s->user_scores);
131 char states[MAXLVL + 1];
133 if ((fin = fopen(fn, "r")))
135 res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
137 for (i = 0; i < s->count && res; i++)
142 level_v[i].is_locked = 1;
143 level_v[i].is_completed = 0;
147 level_v[i].is_locked = 0;
148 level_v[i].is_completed = 1;
152 level_v[i].is_locked = 0;
153 level_v[i].is_completed = 0;
162 get_score(fin, &s->time_score) &&
163 get_score(fin, &s->coin_score);
165 for (i = 0; i < s->count && res; i++)
168 res = get_score(fin, &l->score.best_times) &&
169 get_score(fin, &l->score.unlock_goal) &&
170 get_score(fin, &l->score.most_coins);
176 if (!res && errno != ENOENT)
179 L_("Error while loading user high-score file '%s': %s\n"),
180 fn, errno ? strerror(errno) : L_("Incorrect format"));
184 /*---------------------------------------------------------------------------*/
186 static int set_load(struct set *s, const char *filename)
189 char *scores, *level_name;
191 fin = fopen(config_data(filename), "r");
195 fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
196 filename, strerror(errno));
200 memset(s, 0, sizeof (struct set));
202 /* Set some sane values in case the scores are missing. */
204 score_init_hs(&s->time_score, 359999, 0);
205 score_init_hs(&s->coin_score, 359999, 0);
207 strncpy(s->file, filename, PATHMAX - 1);
209 if (read_line(&s->name, fin) &&
210 read_line(&s->desc, fin) &&
211 read_line(&s->id, fin) &&
212 read_line(&s->shot, fin) &&
213 read_line(&scores, fin))
215 sscanf(scores, "%d %d %d %d %d %d",
216 &s->time_score.timer[0],
217 &s->time_score.timer[1],
218 &s->time_score.timer[2],
219 &s->coin_score.coins[0],
220 &s->coin_score.coins[1],
221 &s->coin_score.coins[2]);
225 strncpy(s->user_scores, "neverballhs-", PATHMAX - 1);
226 strncat(s->user_scores, s->id, PATHMAX - 1 - strlen("neverballhs-"));
230 while (s->count < MAXLVL && read_line(&level_name, fin))
232 s->level_name_v[s->count] = level_name;
262 if ((fin = fopen(config_data(SET_FILE), "r")))
264 while (count < MAXSET && read_line(&name, fin))
266 if (set_load(&set_v[count], name))
283 for (i = 0; i < count; i++)
290 for (j = 0; j < set_v[i].count; j++)
291 free(set_v[i].level_name_v[j]);
297 /*---------------------------------------------------------------------------*/
299 int set_exists(int i)
301 return (0 <= i && i < count);
304 const char *set_name(int i)
306 return set_exists(i) ? _(set_v[i].name) : NULL;
309 const char *set_desc(int i)
311 return set_exists(i) ? _(set_v[i].desc) : NULL;
314 const char *set_shot(int i)
316 return set_exists(i) ? set_v[i].shot : NULL;
319 const struct score *set_time_score(int i)
321 return set_exists(i) ? &set_v[i].time_score : NULL;
324 const struct score *set_coin_score(int i)
326 return set_exists(i) ? &set_v[i].coin_score : NULL;
329 /*---------------------------------------------------------------------------*/
331 int set_level_exists(int s, int i)
333 return (i >= 0 && i < set_v[s].count);
336 static void set_load_levels(void)
343 const char *roman[] = {
345 "I", "II", "III", "IV", "V",
346 "VI", "VII", "VIII", "IX", "X",
347 "XI", "XII", "XIII", "XIV", "XV",
348 "XVI", "XVII", "XVIII", "XIX", "XX",
349 "XXI", "XXII", "XXIII", "XXIV", "XXV"
352 for (i = 0; i < set_v[set].count; i++)
356 level_load(set_v[set].level_name_v[i], l);
358 l->set = &set_v[set];
362 sprintf(l->repr, "%s", roman[bnb++]);
364 sprintf(l->repr, "%02d", nb++);
370 /* Unlock first level. */
372 level_v[0].is_locked = 0;
388 const struct level *get_level(int i)
390 return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
393 /*---------------------------------------------------------------------------*/
395 /* Update the level score rank according to coins and timer. */
396 static int level_score_update(struct level_game *lg, const char *player)
398 int timer = lg->timer;
399 int coins = lg->coins;
400 struct level *l = &level_v[lg->level->number];
402 lg->time_rank = score_time_insert(&l->score.best_times,
403 player, timer, coins);
405 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
406 lg->goal_rank = score_time_insert(&l->score.unlock_goal,
407 player, timer, coins);
411 lg->coin_rank = score_coin_insert(&l->score.most_coins,
412 player, timer, coins);
414 return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
417 /* Update the set score rank according to score and times. */
418 static int set_score_update(struct level_game *lg, const char *player)
420 int timer = lg->times;
421 int coins = lg->score;
422 struct set *s = &set_v[set];
424 lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
425 lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
427 return (lg->score_rank < 3 || lg->times_rank < 3);
430 /* Update the player name for set and level high-score. */
431 void score_change_name(struct level_game *lg, const char *player)
433 struct set *s = &set_v[set];
434 struct level *l = &level_v[lg->level->number];
436 strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
437 strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
438 strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
440 strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
441 strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
446 static struct level *next_level(int i)
448 return set_level_exists(set, i + 1) ? &level_v[i + 1] : NULL;
451 static struct level *next_normal_level(int i)
453 for (i++; i < set_v[set].count; i++)
454 if (!level_v[i].is_bonus)
460 /*---------------------------------------------------------------------------*/
462 void set_finish_level(struct level_game *lg, const char *player)
464 struct set *s = &set_v[set];
465 int ln = lg->level->number; /* Current level number */
466 struct level *cl = &level_v[ln]; /* Current level */
467 struct level *nl = NULL; /* Next level */
468 int dirty = 0; /* Should the score be saved? */
470 assert(s == cl->set);
472 /* if no set, no next level */
475 /* if no set, return */
476 lg->next_level = NULL;
480 /* On level completed */
481 if (lg->status == GAME_GOAL)
483 /* Update level scores */
484 dirty = level_score_update(lg, player);
486 /* Complete the level */
487 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
489 /* Complete the level */
490 if (!cl->is_completed)
492 cl->is_completed = 1;
498 /* On goal reached */
499 if (lg->status == GAME_GOAL)
501 /* Identify the following level */
507 /* Skip bonuses if unlocked in any mode */
511 if (lg->mode == MODE_CHALLENGE && nl->is_locked)
516 lg->bonus_repr = nl->repr;
519 nl = next_normal_level(nl->number);
521 if (nl == NULL && lg->mode == MODE_CHALLENGE)
527 else if (lg->mode == MODE_CHALLENGE)
530 else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
532 /* On fail, identify the next level (only in bonus for challenge) */
533 nl = next_normal_level(ln);
534 /* Next level may be unavailable */
535 if (!cl->is_bonus && nl != NULL && nl->is_locked)
537 /* Fail a bonus level but win the set! */
538 else if (nl == NULL && lg->mode == MODE_CHALLENGE)
545 /* update set score */
546 set_score_update(lg, player);
547 /* unlock all levels */
552 /* unlock the next level if needed */
553 if (nl != NULL && nl->is_locked)
555 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
565 /* got the next level */
573 /*---------------------------------------------------------------------------*/
575 void level_snap(int i)
577 char filename[MAXSTR];
580 /* Convert the level name to a PNG filename. */
582 memset(filename, 0, MAXSTR);
584 ext = strrchr(level_v[i].file, '.');
585 strncpy(filename, level_v[i].file,
586 ext ? ext - level_v[i].file : strlen(level_v[i].file));
587 strcat(filename, ".png");
589 /* Initialize the game for a snapshot. */
591 if (game_init(&level_v[i], 0, 0))
593 /* Render the level and grab the screen. */
599 SDL_GL_SwapBuffers();
601 image_snap(filename);
609 for (i = 0; i < set_v[set].count; i++)
610 level_v[i].is_locked = 0;
613 /*---------------------------------------------------------------------------*/