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 /*---------------------------------------------------------------------------*/
32 char time_n[4][MAXNAM];
36 char coin_n[4][MAXNAM];
42 static int count; /* Number of levels */
43 static int limit; /* Last opened (locked) level */
45 static struct level level_v[MAXLVL];
46 static struct score score_v[MAXLVL];
48 static char scores_file[MAXSTR];
50 static struct level_game current_level_game;
52 /*---------------------------------------------------------------------------*/
54 static void level_store_hs(const char *filename)
58 if ((fout = fopen(config_user(filename), "w")))
63 for (i = 0; i < limit; i++)
64 for (j = 0; j < 3; j++)
66 if (strlen(score_v[i].time_n[j]) == 0)
67 strcpy(score_v[i].time_n[j], DEFAULT_PLAYER);
68 if (strlen(score_v[i].coin_n[j]) == 0)
69 strcpy(score_v[i].coin_n[j], DEFAULT_PLAYER);
71 fprintf(fout, "%d %d %s\n",
74 score_v[i].time_n[j]);
75 fprintf(fout, "%d %d %s\n",
78 score_v[i].coin_n[j]);
85 static void level_load_hs(const char *filename)
91 if ((fin = fopen(config_user(filename), "r")))
95 for (i = 0; i < count; i++)
97 if (fscanf(fin, "%d %d %s",
98 &score_v[i].time_t[0],
99 &score_v[i].time_c[0],
100 score_v[i].time_n[0]) == 3 &&
101 fscanf(fin, "%d %d %s",
102 &score_v[i].coin_t[0],
103 &score_v[i].coin_c[0],
104 score_v[i].coin_n[0]) == 3 &&
105 fscanf(fin, "%d %d %s",
106 &score_v[i].time_t[1],
107 &score_v[i].time_c[1],
108 score_v[i].time_n[1]) == 3 &&
109 fscanf(fin, "%d %d %s",
110 &score_v[i].coin_t[1],
111 &score_v[i].coin_c[1],
112 score_v[i].coin_n[1]) == 3 &&
113 fscanf(fin, "%d %d %s",
114 &score_v[i].time_t[2],
115 &score_v[i].time_c[2],
116 score_v[i].time_n[2]) == 3 &&
117 fscanf(fin, "%d %d %s",
118 &score_v[i].coin_t[2],
119 &score_v[i].coin_c[2],
120 score_v[i].coin_n[2]) == 3)
128 /*---------------------------------------------------------------------------*/
130 static void level_init_rc(const char *filename)
138 /* Load the levels list. */
140 if ((fin = fopen(config_data(filename), "r")))
142 while (count < MAXLVL && fgets(buf, MAXSTR, fin))
144 sscanf(buf, "%s %s %s %s %d %d %s",
149 &level_v[count].time,
150 &level_v[count].goal,
151 level_v[count].song);
152 level_load(config_data(name), &level_v[count]);
159 static void level_init_hs(const char *filename)
165 /* Set some sane values in case the scores file is missing. */
167 for (i = 0; i < MAXLVL; i++)
169 strcpy(score_v[i].time_n[0], "Hard");
170 strcpy(score_v[i].time_n[1], "Medium");
171 strcpy(score_v[i].time_n[2], "Easy");
173 score_v[i].time_t[0] = i ? 59999 : 359999;
174 score_v[i].time_t[1] = i ? 59999 : 359999;
175 score_v[i].time_t[2] = i ? 59999 : 359999;
177 score_v[i].time_c[0] = 0;
178 score_v[i].time_c[1] = 0;
179 score_v[i].time_c[2] = 0;
181 strcpy(score_v[i].coin_n[0], "Hard");
182 strcpy(score_v[i].coin_n[1], "Medium");
183 strcpy(score_v[i].coin_n[2], "Easy");
185 score_v[i].coin_t[0] = i ? 59999 : 359999;
186 score_v[i].coin_t[1] = i ? 59999 : 359999;
187 score_v[i].coin_t[2] = i ? 59999 : 359999;
189 score_v[i].coin_c[0] = 0;
190 score_v[i].coin_c[1] = 0;
191 score_v[i].coin_c[2] = 0;
194 /* Load the default high scores file. */
196 if ((fin = fopen(config_data(filename), "r")))
198 for (i = 0; i < MAXLVL && fgets(buf, MAXSTR, fin); i++)
199 sscanf(buf, "%d %d %d %d %d %d",
200 &score_v[i].time_t[0], &score_v[i].coin_c[0],
201 &score_v[i].time_t[1], &score_v[i].coin_c[1],
202 &score_v[i].time_t[2], &score_v[i].coin_c[2]);
208 /*---------------------------------------------------------------------------*/
210 const char *level_shot(int i)
212 return level_v[i].shot;
215 const char *level_time_n(int i, int j)
217 return score_v[i].time_n[j];
220 const char *level_coin_n(int i, int j)
222 return score_v[i].coin_n[j];
225 /*---------------------------------------------------------------------------*/
226 /* Return the coin count for the Most Coins or Best Time score. */
228 int level_coin_c(int i, int j)
230 return score_v[i].coin_c[j];
233 int level_time_c(int i, int j)
235 return score_v[i].time_c[j];
238 /*---------------------------------------------------------------------------*/
239 /* Return the time for the Most Coins or Best Time score. */
241 int level_coin_t(int i, int j)
243 return score_v[i].coin_t[j];
246 int level_time_t(int i, int j)
248 return score_v[i].time_t[j];
251 /*---------------------------------------------------------------------------*/
253 void level_init(const char *init_levels,
254 const char *init_scores,
255 const char *user_scores)
257 memset(level_v, 0, sizeof (struct level) * MAXLVL);
258 memset(score_v, 0, sizeof (struct score) * MAXLVL);
260 level_init_rc(init_levels);
261 level_init_hs(init_scores);
262 level_load_hs(user_scores);
264 strncpy(scores_file, user_scores, MAXSTR);
267 void level_cheat(void)
268 /* Open each level of the set */
273 void level_free(void)
275 level_store_hs(scores_file);
279 int level_exists(int i)
281 return (0 < i && i < count);
284 int level_opened(int i)
286 return level_exists(i) && (0 < i && i < count && i <= limit);
289 int level_locked(int i)
291 return level_opened(i) && (i == limit);
294 int level_extra_bonus(int i)
296 return level_exists(i) && (i > 20);
299 int level_extra_bonus_opened(void)
301 return level_opened(21);
304 int level_set_completed(void)
306 return limit >= count;
309 static const char * names[] = {"1", "2", "3", "4", "5",
310 "6", "7", "8", "9", "10",
311 "11", "12", "13", "14", "15",
312 "16", "17", "18", "19", "20",
313 N_("B1"), N_("B2"), N_("B3"), N_("B4"), N_("B5")};
315 const char * level_number_name(i)
316 /* Return the number name of the level i */
321 /*---------------------------------------------------------------------------*/
323 int curr_count(void) { return count; }
325 /*---------------------------------------------------------------------------*/
327 static int score_time_comp(const struct score *S, int i, int j)
329 if (S->time_t[i] < S->time_t[j])
332 if (S->time_t[i] == S->time_t[j] &&
333 S->time_c[i] > S->time_c[j])
339 static int score_coin_comp(const struct score *S, int i, int j)
341 if (S->coin_c[i] > S->coin_c[j])
344 if (S->coin_c[i] == S->coin_c[j] &&
345 S->coin_t[i] < S->coin_t[j])
351 /*---------------------------------------------------------------------------*/
353 static void score_time_swap(struct score *S, int i, int j)
359 strncpy(n, S->time_n[i], MAXNAM);
360 strncpy(S->time_n[i], S->time_n[j], MAXNAM);
361 strncpy(S->time_n[j], n, MAXNAM);
364 S->time_t[i] = S->time_t[j];
368 S->time_c[i] = S->time_c[j];
372 static void score_coin_swap(struct score *S, int i, int j)
378 strncpy(n, S->coin_n[i], MAXNAM);
379 strncpy(S->coin_n[i], S->coin_n[j], MAXNAM);
380 strncpy(S->coin_n[j], n, MAXNAM);
383 S->coin_t[i] = S->coin_t[j];
387 S->coin_c[i] = S->coin_c[j];
391 /*---------------------------------------------------------------------------*/
393 int level_replay(const char *filename)
395 return demo_replay_init(filename, ¤t_level_game);
399 int level_play_go(void)
400 /* Start to play the current level */
402 struct level_game *lg = ¤t_level_game;
404 struct level *l = &level_v[lg->level];
406 lg->goal = (mode == MODE_PRACTICE) ? 0 : l->goal;
407 lg->time = (mode == MODE_PRACTICE) ? 0 : l->time;
409 return demo_play_init(USER_REPLAY_FILE, l, lg);
412 void level_play_single(const char *filename)
413 /* Prepare to play a single level */
416 level_init("", "", "");
419 current_level_game.mode = MODE_SINGLE;
420 current_level_game.level = level;
422 strncpy(level_v[0].file, filename, MAXSTR);
423 level_v[level].back[0] = '\0';
424 level_v[level].grad[0] = '\0';
425 level_v[level].song[0] = '\0';
426 level_v[level].shot[0] = '\0';
427 level_v[level].goal = 0;
428 level_v[level].time = 0;
431 void level_play(int i, int m)
432 /* Prepare to play a level sequence from the `i'th level */
434 current_level_game.mode = m;
435 current_level_game.level = i;
437 current_level_game.score = 0;
438 current_level_game.balls = 3;
439 current_level_game.times = 0;
442 /*---------------------------------------------------------------------------*/
444 const struct level_game * curr_lg(void)
446 return ¤t_level_game;
449 int count_extra_balls(int old_score, int coins)
451 int modulo = old_score % 100;
452 int sum = modulo + coins;
456 void level_stop(int state, int clock, int coins)
457 /* Stop the current playing level */
459 struct level_game * lg = ¤t_level_game;
461 int level = lg->level;
462 int timer = (mode == MODE_PRACTICE || mode == MODE_SINGLE) ? clock : lg->time - clock;
468 /* open next level */
469 if (state == GAME_GOAL && mode != MODE_PRACTICE && mode != MODE_SINGLE && limit < level+1)
470 if (level_extra_bonus_opened() || !level_extra_bonus(level+1) || mode == MODE_CHALLENGE)
473 if (mode == MODE_CHALLENGE)
478 /* sum coins an earn extra balls */
479 if (state == GAME_GOAL)
481 lg->balls += count_extra_balls(lg->score, coins);
486 if (state == GAME_TIME || state == GAME_FALL)
490 /* stop demo recording */
496 int mode = current_level_game.mode;
497 int balls = current_level_game.balls;
498 return (mode == MODE_CHALLENGE) && (balls <= 0);
503 int level = current_level_game.level;
504 return (level + 1 == count) || (level_extra_bonus(level + 1));
507 void level_next(void)
509 current_level_game.level++;
512 int level_sort(int *time_i, int *coin_i)
514 int i, timer, coins, level;
517 coins = current_level_game.coins;
518 timer = current_level_game.timer;
519 level = current_level_game.level;
521 config_get_s(CONFIG_PLAYER, player, MAXNAM);
523 /* Insert the time record into the high score list. */
525 strncpy(score_v[level].time_n[3], player, MAXNAM);
526 score_v[level].time_c[3] = coins;
527 score_v[level].time_t[3] = timer;
529 for (i = 2; i >= 0 && score_time_comp(score_v + level, i + 1, i); i--)
531 score_time_swap(score_v + level, i + 1, i);
535 /* Insert the coin record into the high score list. */
537 strncpy(score_v[level].coin_n[3], player, MAXNAM);
538 score_v[level].coin_c[3] = coins;
539 score_v[level].coin_t[3] = timer;
541 for (i = 2; i >= 0 && score_coin_comp(score_v + level, i + 1, i); i--)
543 score_coin_swap(score_v + level, i + 1, i);
547 return (*time_i < 3 || *coin_i < 3);
550 int level_done(int *time_i, int *coin_i)
554 int score = current_level_game.score;
555 int times = current_level_game.times;
557 config_get_s(CONFIG_PLAYER, player, MAXNAM);
559 /* Note a global high score. */
561 strncpy(score_v[0].time_n[3], player, MAXNAM);
562 score_v[0].time_c[3] = score;
563 score_v[0].time_t[3] = times;
565 strncpy(score_v[0].coin_n[3], player, MAXNAM);
566 score_v[0].coin_c[3] = score;
567 score_v[0].coin_t[3] = times;
569 /* Insert the time record into the global high score list. */
571 for (i = 2; i >= 0 && score_time_comp(score_v, i + 1, i); i--)
573 score_time_swap(score_v, i + 1, i);
577 /* Insert the coin record into the global high score list. */
579 for (i = 2; i >= 0 && score_coin_comp(score_v, i + 1, i); i--)
581 score_coin_swap(score_v, i + 1, i);
585 return (*time_i < 3 || *coin_i < 3);
588 /*---------------------------------------------------------------------------*/
590 void level_name(int i, const char *name, int time_i, int coin_i)
592 strncpy(score_v[i].time_n[time_i], name, MAXNAM);
593 strncpy(score_v[i].coin_n[coin_i], name, MAXNAM);
596 void level_snap(int i)
598 char filename[MAXSTR];
600 /* Convert the level name to a BMP filename. */
602 memset(filename, 0, MAXSTR);
603 strncpy(filename, level_v[i].file, strcspn(level_v[i].file, "."));
604 strcat(filename, ".bmp");
606 /* Initialize the game for a snapshot. */
608 if (game_init(&level_v[i], 0, 0))
610 /* Render the level and grab the screen. */
616 SDL_GL_SwapBuffers();
618 image_snap(filename, config_get_d(CONFIG_WIDTH), config_get_d(CONFIG_HEIGHT));
622 /*---------------------------------------------------------------------------*/