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 #define SET_GET(a, i) ((struct set *) array_get((a), (i)))
60 static struct level level_v[MAXLVL];
62 /*---------------------------------------------------------------------------*/
64 static void put_score(fs_file fp, const struct score *s)
68 for (j = 0; j < NSCORE; j++)
69 fs_printf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
72 void set_store_hs(void)
74 const struct set *s = SET_GET(sets, curr);
77 const struct level *l;
78 char states[MAXLVL + 1];
80 if ((fout = fs_open(config_cheat() ?
82 s->user_scores, "w")))
84 for (i = 0; i < s->count; i++)
86 if (level_v[i].is_locked)
88 else if (level_v[i].is_completed)
93 states[s->count] = '\0';
94 fs_printf(fout, "%s\n",states);
96 put_score(fout, &s->time_score);
97 put_score(fout, &s->coin_score);
99 for (i = 0; i < s->count; i++)
103 put_score(fout, &l->score.best_times);
104 put_score(fout, &l->score.fast_unlock);
105 put_score(fout, &l->score.most_coins);
112 static int get_score(fs_file fp, struct score *s)
118 for (j = 0; j < NSCORE && res; j++)
120 res = (fs_gets(line, sizeof (line), fp) &&
121 sscanf(line, "%d %d %s\n",
129 /* Get the score of the set. */
130 static void set_load_hs(void)
132 struct set *s = SET_GET(sets, curr);
137 const char *fn = config_cheat() ? s->cheat_scores : s->user_scores;
138 char states[MAXLVL + sizeof ("\n")];
140 if ((fin = fs_open(fn, "r")))
142 res = (fs_gets(states, sizeof (states), fin) &&
143 strlen(states) - 1 == s->count);
145 for (i = 0; i < s->count && res; i++)
150 level_v[i].is_locked = 1;
151 level_v[i].is_completed = 0;
155 level_v[i].is_locked = 0;
156 level_v[i].is_completed = 1;
160 level_v[i].is_locked = 0;
161 level_v[i].is_completed = 0;
170 get_score(fin, &s->time_score) &&
171 get_score(fin, &s->coin_score);
173 for (i = 0; i < s->count && res; i++)
176 res = get_score(fin, &l->score.best_times) &&
177 get_score(fin, &l->score.fast_unlock) &&
178 get_score(fin, &l->score.most_coins);
184 if (!res && errno != ENOENT)
187 L_("Error while loading user high-score file '%s': %s\n"),
188 fn, errno ? strerror(errno) : L_("Incorrect format"));
192 /*---------------------------------------------------------------------------*/
194 static int set_load(struct set *s, const char *filename)
197 char *scores, *level_name;
199 /* Skip "Misc" set when not in dev mode. */
201 if (strcmp(filename, SET_MISC) == 0 && !config_cheat())
204 fin = fs_open(filename, "r");
208 fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
209 filename, strerror(errno));
213 memset(s, 0, sizeof (struct set));
215 /* Set some sane values in case the scores are missing. */
217 score_init_hs(&s->time_score, 359999, 0);
218 score_init_hs(&s->coin_score, 359999, 0);
220 strncpy(s->file, filename, PATHMAX - 1);
222 if (read_line(&s->name, fin) &&
223 read_line(&s->desc, fin) &&
224 read_line(&s->id, fin) &&
225 read_line(&s->shot, fin) &&
226 read_line(&scores, fin))
228 sscanf(scores, "%d %d %d %d %d %d",
229 &s->time_score.timer[0],
230 &s->time_score.timer[1],
231 &s->time_score.timer[2],
232 &s->coin_score.coins[0],
233 &s->coin_score.coins[1],
234 &s->coin_score.coins[2]);
238 s->user_scores = concat_string("Scores/", s->id, ".txt", NULL);
239 s->cheat_scores = concat_string("Scores/", s->id, "-cheat.txt", NULL);
243 while (s->count < MAXLVL && read_line(&level_name, fin))
245 s->level_name_v[s->count] = level_name;
264 static void set_free(struct set *s)
273 free(s->user_scores);
274 free(s->cheat_scores);
276 for (i = 0; i < s->count; i++)
277 free(s->level_name_v[i]);
280 /*---------------------------------------------------------------------------*/
282 static int cmp_dir_items(const void *A, const void *B)
284 const struct dir_item *a = A, *b = B;
285 return strcmp(a->path, b->path);
288 static int set_is_loaded(const char *path)
292 for (i = 0; i < array_len(sets); i++)
293 if (strcmp(SET_GET(sets, i)->file, path) == 0)
299 static int is_unseen_set(struct dir_item *item)
301 return (strncmp(base_name(item->path, NULL), "set-", 4) == 0 &&
302 !set_is_loaded(item->path));
316 sets = array_new(sizeof (struct set));
320 * First, load the sets listed in the set file, preserving order.
323 if ((fin = fs_open(SET_FILE, "r")))
325 while (read_line(&name, fin))
327 struct set *s = array_add(sets);
329 if (!set_load(s, name))
338 * Then, scan for any remaining set description files, and add
339 * them after the first group in alphabetic order.
342 if ((items = fs_dir_scan("", is_unseen_set)))
344 array_sort(items, cmp_dir_items);
346 for (i = 0; i < array_len(items); i++)
348 struct set *s = array_add(sets);
350 if (!set_load(s, DIR_ITEM_GET(items, i)->path))
357 return array_len(sets);
364 for (i = 0; i < array_len(sets); i++)
365 set_free(array_get(sets, i));
371 /*---------------------------------------------------------------------------*/
373 int set_exists(int i)
375 return (0 <= i && i < array_len(sets));
378 const char *set_id(int i)
380 return set_exists(i) ? SET_GET(sets, i)->id : NULL;
383 const char *set_name(int i)
385 return set_exists(i) ? _(SET_GET(sets, i)->name) : NULL;
388 const char *set_desc(int i)
390 return set_exists(i) ? _(SET_GET(sets, i)->desc) : NULL;
393 const char *set_shot(int i)
395 return set_exists(i) ? SET_GET(sets, i)->shot : NULL;
398 const struct score *set_time_score(int i)
400 return set_exists(i) ? &SET_GET(sets, i)->time_score : NULL;
403 const struct score *set_coin_score(int i)
405 return set_exists(i) ? &SET_GET(sets, i)->coin_score : NULL;
408 /*---------------------------------------------------------------------------*/
410 int set_level_exists(int i, int l)
412 return (l >= 0 && l < SET_GET(sets, i)->count);
415 static void set_load_levels(void)
422 const char *roman[] = {
424 "I", "II", "III", "IV", "V",
425 "VI", "VII", "VIII", "IX", "X",
426 "XI", "XII", "XIII", "XIV", "XV",
427 "XVI", "XVII", "XVIII", "XIX", "XX",
428 "XXI", "XXII", "XXIII", "XXIV", "XXV"
431 for (i = 0; i < SET_GET(sets, curr)->count; i++)
435 level_load(SET_GET(sets, curr)->level_name_v[i], l);
437 l->set = SET_GET(sets, curr);
441 sprintf(l->name, "%s", roman[bnb++]);
443 sprintf(l->name, "%02d", nb++);
449 /* Unlock first level. */
451 level_v[0].is_locked = 0;
467 struct level *get_level(int i)
469 return (i >= 0 && i < SET_GET(sets, curr)->count) ? &level_v[i] : NULL;
472 /*---------------------------------------------------------------------------*/
474 int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
476 struct set *s = SET_GET(sets, curr);
477 const char *player = config_get_s(CONFIG_PLAYER);
480 *score_rank = score_coin_insert(&s->coin_score, player, timer, coins);
483 *times_rank = score_time_insert(&s->time_score, player, timer, coins);
485 if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
491 void set_rename_player(int score_rank, int times_rank, const char *player)
493 struct set *s = SET_GET(sets, curr);
495 strncpy(s->coin_score.player[score_rank], player, MAXNAM - 1);
496 strncpy(s->time_score.player[times_rank], player, MAXNAM - 1);
499 /*---------------------------------------------------------------------------*/
501 void level_snap(int i, const char *path)
503 char filename[MAXSTR];
505 /* Convert the level name to a PNG filename. */
507 sprintf(filename, "%s/%s.png", path, base_name(level_v[i].file, ".sol"));
509 /* Initialize the game for a snapshot. */
511 if (game_client_init(level_v[i].file))
515 cmd.type = CMD_GOAL_OPEN;
516 game_proxy_enq(&cmd);
518 /* Render the level and grab the screen. */
521 game_set_fly(1.f, game_client_file());
523 game_client_step(NULL);
525 SDL_GL_SwapBuffers();
527 image_snap(filename);
535 for (i = 0; i < SET_GET(sets, curr)->count; i++)
537 level_v[i].is_locked = 0;
538 level_v[i].is_completed = 1;
542 /*---------------------------------------------------------------------------*/