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 #include "game_server.h"
29 #include "game_client.h"
30 #include "game_proxy.h"
32 /*---------------------------------------------------------------------------*/
38 char *id; /* Internal set identifier */
39 char *name; /* Set name */
40 char *desc; /* Set description */
41 char *shot; /* Set screen-shot */
43 char *user_scores; /* User high-score file */
44 char *cheat_scores; /* Cheat mode score file */
46 struct score coin_score; /* Challenge score */
47 struct score time_score; /* Challenge score */
51 int count; /* Number of levels */
52 char *level_name_v[MAXLVL]; /* List of level file names */
55 static int set_state = 0;
60 static struct set set_v[MAXSET];
61 static struct level level_v[MAXLVL];
63 /*---------------------------------------------------------------------------*/
65 static void put_score(fs_file fp, const struct score *s)
69 for (j = 0; j < NSCORE; j++)
70 fs_printf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
73 void set_store_hs(void)
75 const struct set *s = &set_v[set];
78 const struct level *l;
79 char states[MAXLVL + 1];
81 if ((fout = fs_open(config_cheat() ?
83 s->user_scores, "w")))
85 for (i = 0; i < s->count; i++)
87 if (level_v[i].is_locked)
89 else if (level_v[i].is_completed)
94 states[s->count] = '\0';
95 fs_printf(fout, "%s\n",states);
97 put_score(fout, &s->time_score);
98 put_score(fout, &s->coin_score);
100 for (i = 0; i < s->count; i++)
104 put_score(fout, &l->score.best_times);
105 put_score(fout, &l->score.fast_unlock);
106 put_score(fout, &l->score.most_coins);
113 static int get_score(fs_file fp, struct score *s)
119 for (j = 0; j < NSCORE && res; j++)
121 res = (fs_gets(line, sizeof (line), fp) &&
122 sscanf(line, "%d %d %s\n",
130 /* Get the score of the set. */
131 static void set_load_hs(void)
133 struct set *s = &set_v[set];
138 const char *fn = config_cheat() ? s->cheat_scores : s->user_scores;
139 char states[MAXLVL + sizeof ("\n")];
141 if ((fin = fs_open(fn, "r")))
143 res = (fs_gets(states, sizeof (states), fin) &&
144 strlen(states) - 1 == s->count);
146 for (i = 0; i < s->count && res; i++)
151 level_v[i].is_locked = 1;
152 level_v[i].is_completed = 0;
156 level_v[i].is_locked = 0;
157 level_v[i].is_completed = 1;
161 level_v[i].is_locked = 0;
162 level_v[i].is_completed = 0;
171 get_score(fin, &s->time_score) &&
172 get_score(fin, &s->coin_score);
174 for (i = 0; i < s->count && res; i++)
177 res = get_score(fin, &l->score.best_times) &&
178 get_score(fin, &l->score.fast_unlock) &&
179 get_score(fin, &l->score.most_coins);
185 if (!res && errno != ENOENT)
188 L_("Error while loading user high-score file '%s': %s\n"),
189 fn, errno ? strerror(errno) : L_("Incorrect format"));
193 /*---------------------------------------------------------------------------*/
195 static int set_load(struct set *s, const char *filename)
198 char *scores, *level_name;
200 /* Skip "Misc" set when not in dev mode. */
202 if (strcmp(filename, SET_MISC) == 0 && !config_cheat())
205 fin = fs_open(filename, "r");
209 fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
210 filename, strerror(errno));
214 memset(s, 0, sizeof (struct set));
216 /* Set some sane values in case the scores are missing. */
218 score_init_hs(&s->time_score, 359999, 0);
219 score_init_hs(&s->coin_score, 359999, 0);
221 strncpy(s->file, filename, PATHMAX - 1);
223 if (read_line(&s->name, fin) &&
224 read_line(&s->desc, fin) &&
225 read_line(&s->id, fin) &&
226 read_line(&s->shot, fin) &&
227 read_line(&scores, fin))
229 sscanf(scores, "%d %d %d %d %d %d",
230 &s->time_score.timer[0],
231 &s->time_score.timer[1],
232 &s->time_score.timer[2],
233 &s->coin_score.coins[0],
234 &s->coin_score.coins[1],
235 &s->coin_score.coins[2]);
239 s->user_scores = concat_string("Scores/", s->id, ".txt", NULL);
240 s->cheat_scores = concat_string("Scores/", s->id, "-cheat.txt", NULL);
244 while (s->count < MAXLVL && read_line(&level_name, fin))
246 s->level_name_v[s->count] = level_name;
265 static int cmp_dir_items(const void *A, const void *B)
267 const struct dir_item *a = A, *b = B;
268 return strcmp(a->path, b->path);
271 static int set_is_loaded(const char *path)
275 for (i = 0; i < count; i++)
276 if (strcmp(set_v[i].file, path) == 0)
282 static int is_unseen_set(struct dir_item *item)
284 return (strncmp(base_name(item->path, NULL), "set-", 4) == 0 &&
285 !set_is_loaded(item->path));
303 * First, load the sets listed in the set file, preserving order.
306 if ((fin = fs_open(SET_FILE, "r")))
308 while (count < MAXSET && read_line(&name, fin))
310 if (set_load(&set_v[count], name))
321 * Then, scan for any remaining set description files, and add
322 * them after the first group in alphabetic order.
325 if ((items = fs_dir_scan("", is_unseen_set)))
327 array_sort(items, cmp_dir_items);
329 for (i = 0; i < array_len(items) && count < MAXSET; i++)
330 if (set_load(&set_v[count], DIR_ITEM_GET(items, i)->path))
345 for (i = 0; i < count; i++)
352 free(set_v[i].user_scores);
353 free(set_v[i].cheat_scores);
355 for (j = 0; j < set_v[i].count; j++)
356 free(set_v[i].level_name_v[j]);
362 /*---------------------------------------------------------------------------*/
364 int set_exists(int i)
366 return (0 <= i && i < count);
369 const char *set_id(int i)
371 return set_exists(i) ? set_v[i].id : NULL;
374 const char *set_name(int i)
376 return set_exists(i) ? _(set_v[i].name) : NULL;
379 const char *set_desc(int i)
381 return set_exists(i) ? _(set_v[i].desc) : NULL;
384 const char *set_shot(int i)
386 return set_exists(i) ? set_v[i].shot : NULL;
389 const struct score *set_time_score(int i)
391 return set_exists(i) ? &set_v[i].time_score : NULL;
394 const struct score *set_coin_score(int i)
396 return set_exists(i) ? &set_v[i].coin_score : NULL;
399 /*---------------------------------------------------------------------------*/
401 int set_level_exists(int s, int i)
403 return (i >= 0 && i < set_v[s].count);
406 static void set_load_levels(void)
413 const char *roman[] = {
415 "I", "II", "III", "IV", "V",
416 "VI", "VII", "VIII", "IX", "X",
417 "XI", "XII", "XIII", "XIV", "XV",
418 "XVI", "XVII", "XVIII", "XIX", "XX",
419 "XXI", "XXII", "XXIII", "XXIV", "XXV"
422 for (i = 0; i < set_v[set].count; i++)
426 level_load(set_v[set].level_name_v[i], l);
428 l->set = &set_v[set];
432 sprintf(l->name, "%s", roman[bnb++]);
434 sprintf(l->name, "%02d", nb++);
440 /* Unlock first level. */
442 level_v[0].is_locked = 0;
458 struct level *get_level(int i)
460 return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
463 /*---------------------------------------------------------------------------*/
465 int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
467 struct set *s = &set_v[set];
468 char player[MAXSTR] = "";
470 config_get_s(CONFIG_PLAYER, player, MAXSTR);
473 *score_rank = score_coin_insert(&s->coin_score, player, timer, coins);
476 *times_rank = score_time_insert(&s->time_score, player, timer, coins);
478 if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
484 void set_rename_player(int score_rank, int times_rank, const char *player)
486 struct set *s = &set_v[set];
488 strncpy(s->coin_score.player[score_rank], player, MAXNAM);
489 strncpy(s->time_score.player[times_rank], player, MAXNAM);
492 /*---------------------------------------------------------------------------*/
494 void level_snap(int i, const char *path)
496 char filename[MAXSTR];
498 /* Convert the level name to a PNG filename. */
500 sprintf(filename, "%s/%s.png", path, base_name(level_v[i].file, ".sol"));
502 /* Initialize the game for a snapshot. */
504 if (game_client_init(level_v[i].file))
508 cmd.type = CMD_GOAL_OPEN;
509 game_proxy_enq(&cmd);
511 /* Render the level and grab the screen. */
514 game_set_fly(1.f, game_client_file());
516 game_client_step(NULL);
518 SDL_GL_SwapBuffers();
520 image_snap(filename);
528 for (i = 0; i < set_v[set].count; i++)
530 level_v[i].is_locked = 0;
531 level_v[i].is_completed = 1;
535 /*---------------------------------------------------------------------------*/