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 */
38 char *contribution; /* Is the set a contribution? */
40 char user_scores[PATHMAX]; /* User high-score file */
42 struct score time_score; /* Challenge score */
43 struct score coin_score; /* Challenge score */
47 int count; /* Number of levels */
48 char *level_name_v[MAXLVL]; /* List of level file names */
51 static int set_state = 0;
58 static struct set set_v[MAXSET];
59 static struct level level_v[MAXLVL];
61 /*---------------------------------------------------------------------------*/
63 static void put_score(FILE *fp, const struct score *s)
67 for (j = 0; j < NSCORE; j++)
68 fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
71 /* Store the score of the set. */
72 static void set_store_hs(void)
74 const struct set *s = &set_v[set];
77 const struct level *l;
78 char states[MAXLVL + 1];
80 if ((fout = fopen(config_user(s->user_scores), "w")))
82 for (i = 0; i < s->count; i++)
84 if (level_v[i].is_locked)
86 else if (level_v[i].is_completed)
91 states[s->count] = '\0';
92 fprintf(fout, "%s\n",states);
94 put_score(fout, &s->time_score);
95 put_score(fout, &s->coin_score);
97 for (i = 0; i < s->count; i++)
101 put_score(fout, &l->score.best_times);
102 put_score(fout, &l->score.unlock_goal);
103 put_score(fout, &l->score.most_coins);
110 static int get_score(FILE *fp, struct score *s)
115 for (j = 0; j < NSCORE && res; j++)
117 res = fscanf(fp, "%d %d %s\n",
125 /* Get the score of the set. */
126 static void set_load_hs(void)
128 struct set *s = &set_v[set];
133 const char *fn = config_user(s->user_scores);
134 char states[MAXLVL + 1];
136 if ((fin = fopen(fn, "r")))
138 res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
140 for (i = 0; i < s->count && res; i++)
145 level_v[i].is_locked = 1;
146 level_v[i].is_completed = 0;
150 level_v[i].is_locked = 0;
151 level_v[i].is_completed = 1;
155 level_v[i].is_locked = 0;
156 level_v[i].is_completed = 0;
165 get_score(fin, &s->time_score) &&
166 get_score(fin, &s->coin_score);
168 for (i = 0; i < s->count && res; i++)
171 res = get_score(fin, &l->score.best_times) &&
172 get_score(fin, &l->score.unlock_goal) &&
173 get_score(fin, &l->score.most_coins);
179 if (!res && errno != ENOENT)
182 L_("Error while loading user high-score file '%s': %s\n"),
183 fn, errno ? strerror(errno) : L_("Incorrect format"));
187 /*---------------------------------------------------------------------------*/
189 static int set_load(struct set *s, const char *filename)
192 char *scores, *level_name;
194 fin = fopen(config_data(filename), "r");
198 fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
199 filename, strerror(errno));
203 memset(s, 0, sizeof (struct set));
205 /* Set some sane values in case the scores are missing. */
207 score_init_hs(&s->time_score, 359999, 0);
208 score_init_hs(&s->coin_score, 359999, 0);
210 strncpy(s->file, filename, PATHMAX - 1);
212 if (read_line(&s->name, fin) &&
213 read_line(&s->desc, fin) &&
214 read_line(&s->id, fin) &&
215 read_line(&s->shot, fin) &&
216 read_line(&s->contribution, fin) &&
217 read_line(&scores, fin))
219 sscanf(scores, "%d %d %d %d %d %d",
220 &s->time_score.timer[0],
221 &s->time_score.timer[1],
222 &s->time_score.timer[2],
223 &s->coin_score.coins[0],
224 &s->coin_score.coins[1],
225 &s->coin_score.coins[2]);
229 strncpy(s->user_scores, "neverballhs-", PATHMAX - 1);
230 strncat(s->user_scores, s->id, PATHMAX - 1 - strlen("neverballhs-"));
234 while (s->count < MAXLVL && read_line(&level_name, fin))
236 s->level_name_v[s->count] = level_name;
240 retsl = (!strcmp(s->contribution, "0")) ? 0 : 1;
251 free(s->contribution);
258 int set_init(int listContributions)
270 if ((fin = fopen(config_data(SET_FILE), "r")))
272 while (count < MAXSET && read_line(&name, fin))
274 if (set_load(&set_v[count], name))
276 if (((retsl > 0) ? 1 : 0) == ((listContributions > 0) ? 1 : 0))
295 for (i = 0; i < count; i++)
301 free(set_v[i].contribution);
303 for (j = 0; j < set_v[i].count; j++)
304 free(set_v[i].level_name_v[j]);
310 /*---------------------------------------------------------------------------*/
312 /*******************************************
313 * contribValue: can have a *
314 * value of 0, 1 or 2 *
315 * 0: Default behaviour (all) *
316 * 1: Only offical sets will *
318 * 2: Only contributed sets *
320 *******************************************/
322 int set_exists(int i, int contribValue)
324 if (contribValue == 1)
326 if(set_contribution(i)!=NULL && !strcmp(set_contribution(i), "0"))
330 else if (contribValue == 2)
332 if(set_contribution(i)!=NULL && !strcmp(set_contribution(i), "1"))
337 return (0 <= i && i < count);
342 const char *set_name(int i)
344 return set_exists(i, 0) ? _(set_v[i].name) : NULL;
347 const char *set_desc(int i)
349 return set_exists(i, 0) ? _(set_v[i].desc) : NULL;
352 const char *set_shot(int i)
354 return set_exists(i, 0) ? set_v[i].shot : NULL;
357 const struct score *set_time_score(int i)
359 return set_exists(i, 0) ? &set_v[i].time_score : NULL;
362 const struct score *set_coin_score(int i)
364 return set_exists(i, 0) ? &set_v[i].coin_score : NULL;
367 const char *set_contribution(int i)
369 return set_exists(i, 0) ? _(set_v[i].contribution) : NULL;
372 /*---------------------------------------------------------------------------*/
374 int set_level_exists(int s, int i)
376 return (i >= 0 && i < set_v[s].count);
379 static void set_load_levels(void)
386 const char *roman[] = {
388 "I", "II", "III", "IV", "V",
389 "VI", "VII", "VIII", "IX", "X",
390 "XI", "XII", "XIII", "XIV", "XV",
391 "XVI", "XVII", "XVIII", "XIX", "XX",
392 "XXI", "XXII", "XXIII", "XXIV", "XXV"
395 for (i = 0; i < set_v[set].count; i++)
399 level_load(set_v[set].level_name_v[i], l);
401 l->set = &set_v[set];
405 sprintf(l->repr, "%s", roman[bnb++]);
407 sprintf(l->repr, "%02d", nb++);
413 /* Unlock first level. */
415 level_v[0].is_locked = 0;
431 const struct level *get_level(int i)
433 return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
436 /*---------------------------------------------------------------------------*/
438 /* Update the level score rank according to coins and timer. */
439 static int level_score_update(struct level_game *lg, const char *player)
441 int timer = lg->timer;
442 int coins = lg->coins;
443 struct level *l = &level_v[lg->level->number];
445 lg->time_rank = score_time_insert(&l->score.best_times,
446 player, timer, coins);
448 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
449 lg->goal_rank = score_time_insert(&l->score.unlock_goal,
450 player, timer, coins);
454 lg->coin_rank = score_coin_insert(&l->score.most_coins,
455 player, timer, coins);
457 return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
460 /* Update the set score rank according to score and times. */
461 static int set_score_update(struct level_game *lg, const char *player)
463 int timer = lg->times;
464 int coins = lg->score;
465 struct set *s = &set_v[set];
467 lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
468 lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
470 return (lg->score_rank < 3 || lg->times_rank < 3);
473 /* Update the player name for set and level high-score. */
474 void score_change_name(struct level_game *lg, const char *player)
476 struct set *s = &set_v[set];
477 struct level *l = &level_v[lg->level->number];
479 strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
480 strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
481 strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
483 strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
484 strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
489 static struct level *next_level(int i)
491 return set_level_exists(set, i + 1) ? &level_v[i + 1] : NULL;
494 static struct level *next_normal_level(int i)
496 for (i++; i < set_v[set].count; i++)
497 if (!level_v[i].is_bonus)
503 /*---------------------------------------------------------------------------*/
505 void set_finish_level(struct level_game *lg, const char *player)
507 struct set *s = &set_v[set];
508 int ln = lg->level->number; /* Current level number */
509 struct level *cl = &level_v[ln]; /* Current level */
510 struct level *nl = NULL; /* Next level */
511 int dirty = 0; /* Should the score be saved? */
513 assert(s == cl->set);
515 /* if no set, no next level */
518 /* if no set, return */
519 lg->next_level = NULL;
523 /* On level completed */
524 if (lg->status == GAME_GOAL)
526 /* Update level scores */
527 dirty = level_score_update(lg, player);
529 /* Complete the level */
530 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
532 /* Complete the level */
533 if (!cl->is_completed)
535 cl->is_completed = 1;
541 /* On goal reached */
542 if (lg->status == GAME_GOAL)
544 /* Identify the following level */
550 /* Skip bonuses if unlocked in any mode */
554 if (lg->mode == MODE_CHALLENGE && nl->is_locked)
559 lg->bonus_repr = nl->repr;
562 nl = next_normal_level(nl->number);
564 if (nl == NULL && lg->mode == MODE_CHALLENGE)
570 else if (lg->mode == MODE_CHALLENGE)
573 else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
575 /* On fail, identify the next level (only in bonus for challenge) */
576 nl = next_normal_level(ln);
577 /* Next level may be unavailable */
578 if (!cl->is_bonus && nl != NULL && nl->is_locked)
580 /* Fail a bonus level but win the set! */
581 else if (nl == NULL && lg->mode == MODE_CHALLENGE)
588 /* update set score */
589 set_score_update(lg, player);
590 /* unlock all levels */
595 /* unlock the next level if needed */
596 if (nl != NULL && nl->is_locked)
598 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
608 /* got the next level */
616 /*---------------------------------------------------------------------------*/
618 void level_snap(int i)
620 char filename[MAXSTR];
623 /* Convert the level name to a PNG filename. */
625 memset(filename, 0, MAXSTR);
627 ext = strrchr(level_v[i].file, '.');
628 strncpy(filename, level_v[i].file,
629 ext ? ext - level_v[i].file : strlen(level_v[i].file));
630 strcat(filename, ".png");
632 /* Initialize the game for a snapshot. */
634 if (game_init(&level_v[i], 0, 0))
636 /* Render the level and grab the screen. */
642 SDL_GL_SwapBuffers();
644 image_snap(filename);
652 for (i = 0; i < set_v[set].count; i++)
653 level_v[i].is_locked = 0;
656 /*---------------------------------------------------------------------------*/