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)
325 return (set_contribution(i)!=NULL && !strcmp(set_contribution(i), "0")) ? 1 : 0;
327 else if (contribValue == 2)
328 return (set_contribution(i)!=NULL && !strcmp(set_contribution(i), "1")) ? 1 : 0;
331 return (0 <= i && i < count);
336 const char *set_name(int i)
338 return set_exists(i, 0) ? _(set_v[i].name) : NULL;
341 const char *set_desc(int i)
343 return set_exists(i, 0) ? _(set_v[i].desc) : NULL;
346 const char *set_shot(int i)
348 return set_exists(i, 0) ? set_v[i].shot : NULL;
351 const struct score *set_time_score(int i)
353 return set_exists(i, 0) ? &set_v[i].time_score : NULL;
356 const struct score *set_coin_score(int i)
358 return set_exists(i, 0) ? &set_v[i].coin_score : NULL;
361 const char *set_contribution(int i)
363 return set_exists(i, 0) ? _(set_v[i].contribution) : NULL;
366 /*---------------------------------------------------------------------------*/
368 int set_level_exists(int s, int i)
370 return (i >= 0 && i < set_v[s].count);
373 static void set_load_levels(void)
380 const char *roman[] = {
382 "I", "II", "III", "IV", "V",
383 "VI", "VII", "VIII", "IX", "X",
384 "XI", "XII", "XIII", "XIV", "XV",
385 "XVI", "XVII", "XVIII", "XIX", "XX",
386 "XXI", "XXII", "XXIII", "XXIV", "XXV"
389 for (i = 0; i < set_v[set].count; i++)
393 level_load(set_v[set].level_name_v[i], l);
395 l->set = &set_v[set];
399 sprintf(l->repr, "%s", roman[bnb++]);
401 sprintf(l->repr, "%02d", nb++);
407 /* Unlock first level. */
409 level_v[0].is_locked = 0;
425 const struct level *get_level(int i)
427 return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
430 /*---------------------------------------------------------------------------*/
432 /* Update the level score rank according to coins and timer. */
433 static int level_score_update(struct level_game *lg, const char *player)
435 int timer = lg->timer;
436 int coins = lg->coins;
437 struct level *l = &level_v[lg->level->number];
439 lg->time_rank = score_time_insert(&l->score.best_times,
440 player, timer, coins);
442 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
443 lg->goal_rank = score_time_insert(&l->score.unlock_goal,
444 player, timer, coins);
448 lg->coin_rank = score_coin_insert(&l->score.most_coins,
449 player, timer, coins);
451 return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
454 /* Update the set score rank according to score and times. */
455 static int set_score_update(struct level_game *lg, const char *player)
457 int timer = lg->times;
458 int coins = lg->score;
459 struct set *s = &set_v[set];
461 lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
462 lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
464 return (lg->score_rank < 3 || lg->times_rank < 3);
467 /* Update the player name for set and level high-score. */
468 void score_change_name(struct level_game *lg, const char *player)
470 struct set *s = &set_v[set];
471 struct level *l = &level_v[lg->level->number];
473 strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
474 strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
475 strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
477 strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
478 strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
483 static struct level *next_level(int i)
485 return set_level_exists(set, i + 1) ? &level_v[i + 1] : NULL;
488 static struct level *next_normal_level(int i)
490 for (i++; i < set_v[set].count; i++)
491 if (!level_v[i].is_bonus)
497 /*---------------------------------------------------------------------------*/
499 void set_finish_level(struct level_game *lg, const char *player)
501 struct set *s = &set_v[set];
502 int ln = lg->level->number; /* Current level number */
503 struct level *cl = &level_v[ln]; /* Current level */
504 struct level *nl = NULL; /* Next level */
505 int dirty = 0; /* Should the score be saved? */
507 assert(s == cl->set);
509 /* if no set, no next level */
512 /* if no set, return */
513 lg->next_level = NULL;
517 /* On level completed */
518 if (lg->status == GAME_GOAL)
520 /* Update level scores */
521 dirty = level_score_update(lg, player);
523 /* Complete the level */
524 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
526 /* Complete the level */
527 if (!cl->is_completed)
529 cl->is_completed = 1;
535 /* On goal reached */
536 if (lg->status == GAME_GOAL)
538 /* Identify the following level */
544 /* Skip bonuses if unlocked in any mode */
548 if (lg->mode == MODE_CHALLENGE && nl->is_locked)
553 lg->bonus_repr = nl->repr;
556 nl = next_normal_level(nl->number);
558 if (nl == NULL && lg->mode == MODE_CHALLENGE)
564 else if (lg->mode == MODE_CHALLENGE)
567 else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
569 /* On fail, identify the next level (only in bonus for challenge) */
570 nl = next_normal_level(ln);
571 /* Next level may be unavailable */
572 if (!cl->is_bonus && nl != NULL && nl->is_locked)
574 /* Fail a bonus level but win the set! */
575 else if (nl == NULL && lg->mode == MODE_CHALLENGE)
582 /* update set score */
583 set_score_update(lg, player);
584 /* unlock all levels */
589 /* unlock the next level if needed */
590 if (nl != NULL && nl->is_locked)
592 if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
602 /* got the next level */
610 /*---------------------------------------------------------------------------*/
612 void level_snap(int i)
614 char filename[MAXSTR];
617 /* Convert the level name to a PNG filename. */
619 memset(filename, 0, MAXSTR);
621 ext = strrchr(level_v[i].file, '.');
622 strncpy(filename, level_v[i].file,
623 ext ? ext - level_v[i].file : strlen(level_v[i].file));
624 strcat(filename, ".png");
626 /* Initialize the game for a snapshot. */
628 if (game_init(&level_v[i], 0, 0))
630 /* Render the level and grab the screen. */
636 SDL_GL_SwapBuffers();
638 image_snap(filename);
646 for (i = 0; i < set_v[set].count; i++)
647 level_v[i].is_locked = 0;
650 /*---------------------------------------------------------------------------*/