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->scores[SCORE_TIME]);
103 put_score(fout, &l->scores[SCORE_GOAL]);
104 put_score(fout, &l->scores[SCORE_COIN]);
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->scores[SCORE_TIME]) &&
176 get_score(fin, &l->scores[SCORE_GOAL]) &&
177 get_score(fin, &l->scores[SCORE_COIN]));
183 fprintf(stderr, L_("Failure to load user score file '%s'\n"), 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 (str_starts_with(base_name(item->path), "set-") &&
296 str_ends_with(item->path, ".txt") &&
297 !set_is_loaded(item->path));
311 sets = array_new(sizeof (struct set));
315 * First, load the sets listed in the set file, preserving order.
318 if ((fin = fs_open(SET_FILE, "r")))
320 while (read_line(&name, fin))
322 struct set *s = array_add(sets);
324 if (!set_load(s, name))
333 * Then, scan for any remaining set description files, and add
334 * them after the first group in alphabetic order.
337 if ((items = fs_dir_scan("", is_unseen_set)))
339 array_sort(items, cmp_dir_items);
341 for (i = 0; i < array_len(items); i++)
343 struct set *s = array_add(sets);
345 if (!set_load(s, DIR_ITEM_GET(items, i)->path))
352 return array_len(sets);
359 for (i = 0; i < array_len(sets); i++)
360 set_free(array_get(sets, i));
366 /*---------------------------------------------------------------------------*/
368 int set_exists(int i)
370 return (0 <= i && i < array_len(sets));
373 const char *set_id(int i)
375 return set_exists(i) ? SET_GET(sets, i)->id : NULL;
378 const char *set_name(int i)
380 return set_exists(i) ? _(SET_GET(sets, i)->name) : NULL;
383 const char *set_desc(int i)
385 return set_exists(i) ? _(SET_GET(sets, i)->desc) : NULL;
388 const char *set_shot(int i)
390 return set_exists(i) ? SET_GET(sets, i)->shot : NULL;
393 const struct score *set_time_score(int i)
395 return set_exists(i) ? &SET_GET(sets, i)->time_score : NULL;
398 const struct score *set_coin_score(int i)
400 return set_exists(i) ? &SET_GET(sets, i)->coin_score : NULL;
403 /*---------------------------------------------------------------------------*/
405 int set_level_exists(int i, int l)
407 return (l >= 0 && l < SET_GET(sets, i)->count);
410 static void set_load_levels(void)
417 const char *roman[] = {
419 "I", "II", "III", "IV", "V",
420 "VI", "VII", "VIII", "IX", "X",
421 "XI", "XII", "XIII", "XIV", "XV",
422 "XVI", "XVII", "XVIII", "XIX", "XX",
423 "XXI", "XXII", "XXIII", "XXIV", "XXV"
426 for (i = 0; i < SET_GET(sets, curr)->count; i++)
430 level_load(SET_GET(sets, curr)->level_name_v[i], l);
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);
473 score_coin_insert(&s->coin_score, score_rank, player, timer, coins);
474 score_time_insert(&s->time_score, times_rank, player, timer, coins);
476 if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
482 void set_rename_player(int score_rank, int times_rank, const char *player)
484 struct set *s = SET_GET(sets, curr);
486 strncpy(s->coin_score.player[score_rank], player, MAXNAM - 1);
487 strncpy(s->time_score.player[times_rank], player, MAXNAM - 1);
490 /*---------------------------------------------------------------------------*/
492 void level_snap(int i, const char *path)
496 /* Convert the level name to a PNG filename. */
498 filename = concat_string(path,
500 base_name_sans(level_v[i].file, ".sol"),
504 /* Initialize the game for a snapshot. */
506 if (game_client_init(level_v[i].file))
509 cmd.type = CMD_GOAL_OPEN;
510 game_proxy_enq(&cmd);
511 game_client_sync(NULL);
513 /* Render the level and grab the screen. */
516 game_client_fly(1.0f);
518 game_draw(POSE_LEVEL, 0);
519 SDL_GL_SwapBuffers();
521 image_snap(filename);
531 for (i = 0; i < SET_GET(sets, curr)->count; i++)
533 level_v[i].is_locked = 0;
534 level_v[i].is_completed = 1;
538 /*---------------------------------------------------------------------------*/