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.
27 #include "game_server.h"
28 #include "game_client.h"
29 #include "game_proxy.h"
31 /*---------------------------------------------------------------------------*/
37 char *id; /* Internal set identifier */
38 char *name; /* Set name */
39 char *desc; /* Set description */
40 char *shot; /* Set screen-shot */
42 char *user_scores; /* User high-score file */
43 char *cheat_scores; /* Cheat mode 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 #define SET_GET(a, i) ((struct set *) array_get((a), (i)))
59 static struct level level_v[MAXLVL];
61 /*---------------------------------------------------------------------------*/
63 static void put_score(fs_file fp, const struct score *s)
67 for (j = 0; j < NSCORE; j++)
68 fs_printf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
71 void set_store_hs(void)
73 const struct set *s = SET_GET(sets, curr);
76 const struct level *l;
77 char states[MAXLVL + 1];
79 if ((fout = fs_open(config_cheat() ?
81 s->user_scores, "w")))
83 for (i = 0; i < s->count; i++)
85 if (level_v[i].is_locked)
87 else if (level_v[i].is_completed)
92 states[s->count] = '\0';
93 fs_printf(fout, "%s\n",states);
95 put_score(fout, &s->time_score);
96 put_score(fout, &s->coin_score);
98 for (i = 0; i < s->count; i++)
102 put_score(fout, &l->score.best_times);
103 put_score(fout, &l->score.fast_unlock);
104 put_score(fout, &l->score.most_coins);
111 static int get_score(fs_file fp, struct score *s)
117 for (j = 0; j < NSCORE && res; j++)
119 res = (fs_gets(line, sizeof (line), fp) &&
120 sscanf(line, "%d %d %s\n",
128 /* Get the score of the set. */
129 static void set_load_hs(void)
131 struct set *s = SET_GET(sets, curr);
136 const char *fn = config_cheat() ? s->cheat_scores : s->user_scores;
137 char states[MAXLVL + sizeof ("\n")];
139 if ((fin = fs_open(fn, "r")))
141 res = (fs_gets(states, sizeof (states), fin) &&
142 strlen(states) - 1 == s->count);
144 for (i = 0; i < s->count && res; i++)
149 level_v[i].is_locked = 1;
150 level_v[i].is_completed = 0;
154 level_v[i].is_locked = 0;
155 level_v[i].is_completed = 1;
159 level_v[i].is_locked = 0;
160 level_v[i].is_completed = 0;
169 get_score(fin, &s->time_score) &&
170 get_score(fin, &s->coin_score);
172 for (i = 0; i < s->count && res; i++)
175 res = get_score(fin, &l->score.best_times) &&
176 get_score(fin, &l->score.fast_unlock) &&
177 get_score(fin, &l->score.most_coins);
183 fprintf(stderr, L_("Failure to load user score file '%s'"), fn);
187 /*---------------------------------------------------------------------------*/
189 static int set_load(struct set *s, const char *filename)
192 char *scores, *level_name;
194 /* Skip "Misc" set when not in dev mode. */
196 if (strcmp(filename, SET_MISC) == 0 && !config_cheat())
199 fin = fs_open(filename, "r");
203 fprintf(stderr, L_("Failure to load set file '%s'\n"), filename);
207 memset(s, 0, sizeof (struct set));
209 /* Set some sane values in case the scores are missing. */
211 score_init_hs(&s->time_score, 359999, 0);
212 score_init_hs(&s->coin_score, 359999, 0);
214 strncpy(s->file, filename, PATHMAX - 1);
216 if (read_line(&s->name, fin) &&
217 read_line(&s->desc, fin) &&
218 read_line(&s->id, fin) &&
219 read_line(&s->shot, fin) &&
220 read_line(&scores, fin))
222 sscanf(scores, "%d %d %d %d %d %d",
223 &s->time_score.timer[0],
224 &s->time_score.timer[1],
225 &s->time_score.timer[2],
226 &s->coin_score.coins[0],
227 &s->coin_score.coins[1],
228 &s->coin_score.coins[2]);
232 s->user_scores = concat_string("Scores/", s->id, ".txt", NULL);
233 s->cheat_scores = concat_string("Scores/", s->id, "-cheat.txt", NULL);
237 while (s->count < MAXLVL && read_line(&level_name, fin))
239 s->level_name_v[s->count] = level_name;
258 static void set_free(struct set *s)
267 free(s->user_scores);
268 free(s->cheat_scores);
270 for (i = 0; i < s->count; i++)
271 free(s->level_name_v[i]);
274 /*---------------------------------------------------------------------------*/
276 static int cmp_dir_items(const void *A, const void *B)
278 const struct dir_item *a = A, *b = B;
279 return strcmp(a->path, b->path);
282 static int set_is_loaded(const char *path)
286 for (i = 0; i < array_len(sets); i++)
287 if (strcmp(SET_GET(sets, i)->file, path) == 0)
293 static int is_unseen_set(struct dir_item *item)
295 return (strncmp(base_name(item->path, NULL), "set-", 4) == 0 &&
296 !set_is_loaded(item->path));
310 sets = array_new(sizeof (struct set));
314 * First, load the sets listed in the set file, preserving order.
317 if ((fin = fs_open(SET_FILE, "r")))
319 while (read_line(&name, fin))
321 struct set *s = array_add(sets);
323 if (!set_load(s, name))
332 * Then, scan for any remaining set description files, and add
333 * them after the first group in alphabetic order.
336 if ((items = fs_dir_scan("", is_unseen_set)))
338 array_sort(items, cmp_dir_items);
340 for (i = 0; i < array_len(items); i++)
342 struct set *s = array_add(sets);
344 if (!set_load(s, DIR_ITEM_GET(items, i)->path))
351 return array_len(sets);
358 for (i = 0; i < array_len(sets); i++)
359 set_free(array_get(sets, i));
365 /*---------------------------------------------------------------------------*/
367 int set_exists(int i)
369 return (0 <= i && i < array_len(sets));
372 const char *set_id(int i)
374 return set_exists(i) ? SET_GET(sets, i)->id : NULL;
377 const char *set_name(int i)
379 return set_exists(i) ? _(SET_GET(sets, i)->name) : NULL;
382 const char *set_desc(int i)
384 return set_exists(i) ? _(SET_GET(sets, i)->desc) : NULL;
387 const char *set_shot(int i)
389 return set_exists(i) ? SET_GET(sets, i)->shot : NULL;
392 const struct score *set_time_score(int i)
394 return set_exists(i) ? &SET_GET(sets, i)->time_score : NULL;
397 const struct score *set_coin_score(int i)
399 return set_exists(i) ? &SET_GET(sets, i)->coin_score : NULL;
402 /*---------------------------------------------------------------------------*/
404 int set_level_exists(int i, int l)
406 return (l >= 0 && l < SET_GET(sets, i)->count);
409 static void set_load_levels(void)
416 const char *roman[] = {
418 "I", "II", "III", "IV", "V",
419 "VI", "VII", "VIII", "IX", "X",
420 "XI", "XII", "XIII", "XIV", "XV",
421 "XVI", "XVII", "XVIII", "XIX", "XX",
422 "XXI", "XXII", "XXIII", "XXIV", "XXV"
425 for (i = 0; i < SET_GET(sets, curr)->count; i++)
429 level_load(SET_GET(sets, curr)->level_name_v[i], l);
431 l->set = SET_GET(sets, curr);
435 sprintf(l->name, "%s", roman[bnb++]);
437 sprintf(l->name, "%02d", nb++);
443 /* Unlock first level. */
445 level_v[0].is_locked = 0;
461 struct level *get_level(int i)
463 return (i >= 0 && i < SET_GET(sets, curr)->count) ? &level_v[i] : NULL;
466 /*---------------------------------------------------------------------------*/
468 int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
470 struct set *s = SET_GET(sets, curr);
471 const char *player = config_get_s(CONFIG_PLAYER);
474 *score_rank = score_coin_insert(&s->coin_score, player, timer, coins);
477 *times_rank = score_time_insert(&s->time_score, player, timer, coins);
479 if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
485 void set_rename_player(int score_rank, int times_rank, const char *player)
487 struct set *s = SET_GET(sets, curr);
489 strncpy(s->coin_score.player[score_rank], player, MAXNAM - 1);
490 strncpy(s->time_score.player[times_rank], player, MAXNAM - 1);
493 /*---------------------------------------------------------------------------*/
495 void level_snap(int i, const char *path)
497 char filename[MAXSTR];
499 /* Convert the level name to a PNG filename. */
501 sprintf(filename, "%s/%s.png", path, base_name(level_v[i].file, ".sol"));
503 /* Initialize the game for a snapshot. */
505 if (game_client_init(level_v[i].file))
509 cmd.type = CMD_GOAL_OPEN;
510 game_proxy_enq(&cmd);
512 /* Render the level and grab the screen. */
515 game_set_fly(1.f, game_client_file());
517 game_client_step(NULL);
519 SDL_GL_SwapBuffers();
521 image_snap(filename);
529 for (i = 0; i < SET_GET(sets, curr)->count; i++)
531 level_v[i].is_locked = 0;
532 level_v[i].is_completed = 1;
536 /*---------------------------------------------------------------------------*/