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[PATHMAX]; /* User high-score file */
45 struct score coin_score; /* Challenge score */
46 struct score time_score; /* Challenge score */
50 int count; /* Number of levels */
51 char *level_name_v[MAXLVL]; /* List of level file names */
54 static int set_state = 0;
59 static struct set set_v[MAXSET];
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_v[set];
77 const struct level *l;
78 char states[MAXLVL + 1];
80 if ((fout = fs_open(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 fs_printf(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.fast_unlock);
103 put_score(fout, &l->score.most_coins);
110 static int get_score(fs_file fp, struct score *s)
116 for (j = 0; j < NSCORE && res; j++)
118 res = (fs_gets(line, sizeof (line), fp) &&
119 sscanf(line, "%d %d %s\n",
127 /* Get the score of the set. */
128 static void set_load_hs(void)
130 struct set *s = &set_v[set];
135 const char *fn = s->user_scores;
136 char states[MAXLVL + sizeof ("\n")];
138 if ((fin = fs_open(fn, "r")))
140 res = (fs_gets(states, sizeof (states), fin) &&
141 strlen(states) - 1 == s->count);
143 for (i = 0; i < s->count && res; i++)
148 level_v[i].is_locked = 1;
149 level_v[i].is_completed = 0;
153 level_v[i].is_locked = 0;
154 level_v[i].is_completed = 1;
158 level_v[i].is_locked = 0;
159 level_v[i].is_completed = 0;
168 get_score(fin, &s->time_score) &&
169 get_score(fin, &s->coin_score);
171 for (i = 0; i < s->count && res; i++)
174 res = get_score(fin, &l->score.best_times) &&
175 get_score(fin, &l->score.fast_unlock) &&
176 get_score(fin, &l->score.most_coins);
182 if (!res && errno != ENOENT)
185 L_("Error while loading user high-score file '%s': %s\n"),
186 fn, errno ? strerror(errno) : L_("Incorrect format"));
190 /*---------------------------------------------------------------------------*/
192 static int set_load(struct set *s, const char *filename)
195 char *scores, *level_name;
197 /* Skip "Misc" set when not in dev mode. */
199 if (strcmp(filename, SET_MISC) == 0 && !config_cheat())
202 fin = fs_open(filename, "r");
206 fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
207 filename, strerror(errno));
211 memset(s, 0, sizeof (struct set));
213 /* Set some sane values in case the scores are missing. */
215 score_init_hs(&s->time_score, 359999, 0);
216 score_init_hs(&s->coin_score, 359999, 0);
218 strncpy(s->file, filename, PATHMAX - 1);
220 if (read_line(&s->name, fin) &&
221 read_line(&s->desc, fin) &&
222 read_line(&s->id, fin) &&
223 read_line(&s->shot, fin) &&
224 read_line(&scores, fin))
226 sscanf(scores, "%d %d %d %d %d %d",
227 &s->time_score.timer[0],
228 &s->time_score.timer[1],
229 &s->time_score.timer[2],
230 &s->coin_score.coins[0],
231 &s->coin_score.coins[1],
232 &s->coin_score.coins[2]);
236 sprintf(s->user_scores, "Scores/%s.txt", s->id);
240 while (s->count < MAXLVL && read_line(&level_name, fin))
242 s->level_name_v[s->count] = level_name;
261 static int cmp_dir_items(const void *A, const void *B)
263 const struct dir_item *a = A, *b = B;
264 return strcmp(a->path, b->path);
267 static int set_is_loaded(const char *path)
271 for (i = 0; i < count; i++)
272 if (strcmp(set_v[i].file, path) == 0)
278 static int is_unseen_set(struct dir_item *item)
280 return (strncmp(base_name(item->path, NULL), "set-", 4) == 0 &&
281 !set_is_loaded(item->path));
299 * First, load the sets listed in the set file, preserving order.
302 if ((fin = fs_open(SET_FILE, "r")))
304 while (count < MAXSET && read_line(&name, fin))
306 if (set_load(&set_v[count], name))
317 * Then, scan for any remaining set description files, and add
318 * them after the first group in alphabetic order.
321 if ((items = fs_dir_scan("", is_unseen_set)))
323 array_sort(items, cmp_dir_items);
325 for (i = 0; i < array_len(items) && count < MAXSET; i++)
326 if (set_load(&set_v[count], DIR_ITEM_GET(items, i)->path))
341 for (i = 0; i < count; i++)
348 for (j = 0; j < set_v[i].count; j++)
349 free(set_v[i].level_name_v[j]);
355 /*---------------------------------------------------------------------------*/
357 int set_exists(int i)
359 return (0 <= i && i < count);
362 const char *set_id(int i)
364 return set_exists(i) ? set_v[i].id : NULL;
367 const char *set_name(int i)
369 return set_exists(i) ? _(set_v[i].name) : NULL;
372 const char *set_desc(int i)
374 return set_exists(i) ? _(set_v[i].desc) : NULL;
377 const char *set_shot(int i)
379 return set_exists(i) ? set_v[i].shot : NULL;
382 const struct score *set_time_score(int i)
384 return set_exists(i) ? &set_v[i].time_score : NULL;
387 const struct score *set_coin_score(int i)
389 return set_exists(i) ? &set_v[i].coin_score : NULL;
392 /*---------------------------------------------------------------------------*/
394 int set_level_exists(int s, int i)
396 return (i >= 0 && i < set_v[s].count);
399 static void set_load_levels(void)
406 const char *roman[] = {
408 "I", "II", "III", "IV", "V",
409 "VI", "VII", "VIII", "IX", "X",
410 "XI", "XII", "XIII", "XIV", "XV",
411 "XVI", "XVII", "XVIII", "XIX", "XX",
412 "XXI", "XXII", "XXIII", "XXIV", "XXV"
415 for (i = 0; i < set_v[set].count; i++)
419 level_load(set_v[set].level_name_v[i], l);
421 l->set = &set_v[set];
425 sprintf(l->name, "%s", roman[bnb++]);
427 sprintf(l->name, "%02d", nb++);
433 /* Unlock first level. */
435 level_v[0].is_locked = 0;
451 struct level *get_level(int i)
453 return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
456 /*---------------------------------------------------------------------------*/
458 int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
460 struct set *s = &set_v[set];
461 char player[MAXSTR] = "";
463 config_get_s(CONFIG_PLAYER, player, MAXSTR);
466 *score_rank = score_coin_insert(&s->coin_score, player, timer, coins);
469 *times_rank = score_time_insert(&s->time_score, player, timer, coins);
471 if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
477 void set_rename_player(int score_rank, int times_rank, const char *player)
479 struct set *s = &set_v[set];
481 strncpy(s->coin_score.player[score_rank], player, MAXNAM);
482 strncpy(s->time_score.player[times_rank], player, MAXNAM);
485 /*---------------------------------------------------------------------------*/
487 void level_snap(int i, const char *path)
489 char filename[MAXSTR];
491 /* Convert the level name to a PNG filename. */
493 sprintf(filename, "%s/%s.png", path, base_name(level_v[i].file, ".sol"));
495 /* Initialize the game for a snapshot. */
497 if (game_client_init(level_v[i].file))
501 cmd.type = CMD_GOAL_OPEN;
502 game_proxy_enq(&cmd);
504 /* Render the level and grab the screen. */
507 game_set_fly(1.f, game_client_file());
509 game_client_step(NULL);
511 SDL_GL_SwapBuffers();
513 image_snap(filename);
521 for (i = 0; i < set_v[set].count; i++)
523 level_v[i].is_locked = 0;
524 level_v[i].is_completed = 1;
528 /*---------------------------------------------------------------------------*/