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.
31 /*---------------------------------------------------------------------------*/
35 char time_n[4][MAXNAM];
39 char coin_n[4][MAXNAM];
57 static int score; /* Current coin total */
58 static int coins; /* Current coin count */
59 static int balls; /* Current life count */
60 static int goal; /* Current goal count */
62 static int level; /* Current level number */
63 static int count; /* Number of levels */
64 static int limit; /* Last opened (locked) level */
65 static int status; /* Status of current level */
67 static int mode; /* Current play mode */
69 static int level_total;
70 static int coins_total;
71 static int times_total;
73 static struct level level_v[MAXLVL];
74 static struct score score_v[MAXLVL];
76 static char scores_file[MAXSTR];
78 /*---------------------------------------------------------------------------*/
80 static void level_store_hs(const char *filename)
84 if ((fout = fopen(config_user(filename), "w")))
89 for (i = 0; i < limit; i++)
90 for (j = 0; j < 3; j++)
92 if (strlen(score_v[i].time_n[j]) == 0)
93 strcpy(score_v[i].time_n[j], DEFAULT_PLAYER);
94 if (strlen(score_v[i].coin_n[j]) == 0)
95 strcpy(score_v[i].coin_n[j], DEFAULT_PLAYER);
97 fprintf(fout, "%d %d %s\n",
100 score_v[i].time_n[j]);
101 fprintf(fout, "%d %d %s\n",
102 score_v[i].coin_t[j],
103 score_v[i].coin_c[j],
104 score_v[i].coin_n[j]);
111 static void level_load_hs(const char *filename)
117 if ((fin = fopen(config_user(filename), "r")))
121 for (i = 0; i < count; i++)
123 if (fscanf(fin, "%d %d %s",
124 &score_v[i].time_t[0],
125 &score_v[i].time_c[0],
126 score_v[i].time_n[0]) == 3 &&
127 fscanf(fin, "%d %d %s",
128 &score_v[i].coin_t[0],
129 &score_v[i].coin_c[0],
130 score_v[i].coin_n[0]) == 3 &&
131 fscanf(fin, "%d %d %s",
132 &score_v[i].time_t[1],
133 &score_v[i].time_c[1],
134 score_v[i].time_n[1]) == 3 &&
135 fscanf(fin, "%d %d %s",
136 &score_v[i].coin_t[1],
137 &score_v[i].coin_c[1],
138 score_v[i].coin_n[1]) == 3 &&
139 fscanf(fin, "%d %d %s",
140 &score_v[i].time_t[2],
141 &score_v[i].time_c[2],
142 score_v[i].time_n[2]) == 3 &&
143 fscanf(fin, "%d %d %s",
144 &score_v[i].coin_t[2],
145 &score_v[i].coin_c[2],
146 score_v[i].coin_n[2]) == 3)
154 /*---------------------------------------------------------------------------*/
156 static void level_init_rc(const char *filename)
167 /* Load the levels list. */
169 if ((fin = fopen(config_data(filename), "r")))
171 while (count < MAXLVL && fgets(buf, MAXSTR, fin))
173 sscanf(buf, "%s %s %s %s %d %d %s",
178 &level_v[count].time,
179 &level_v[count].goal,
180 level_v[count].song);
187 static void level_init_hs(const char *filename)
193 /* Set some sane values in case the scores file is missing. */
195 for (i = 0; i < MAXLVL; i++)
197 strcpy(score_v[i].time_n[0], "Hard");
198 strcpy(score_v[i].time_n[1], "Medium");
199 strcpy(score_v[i].time_n[2], "Easy");
201 score_v[i].time_t[0] = i ? 59999 : 359999;
202 score_v[i].time_t[1] = i ? 59999 : 359999;
203 score_v[i].time_t[2] = i ? 59999 : 359999;
205 score_v[i].time_c[0] = 0;
206 score_v[i].time_c[1] = 0;
207 score_v[i].time_c[2] = 0;
209 strcpy(score_v[i].coin_n[0], "Hard");
210 strcpy(score_v[i].coin_n[1], "Medium");
211 strcpy(score_v[i].coin_n[2], "Easy");
213 score_v[i].coin_t[0] = i ? 59999 : 359999;
214 score_v[i].coin_t[1] = i ? 59999 : 359999;
215 score_v[i].coin_t[2] = i ? 59999 : 359999;
217 score_v[i].coin_c[0] = 0;
218 score_v[i].coin_c[1] = 0;
219 score_v[i].coin_c[2] = 0;
222 /* Load the default high scores file. */
224 if ((fin = fopen(config_data(filename), "r")))
226 for (i = 0; i < MAXLVL && fgets(buf, MAXSTR, fin); i++)
227 sscanf(buf, "%d %d %d %d %d %d",
228 &score_v[i].time_t[0], &score_v[i].coin_c[0],
229 &score_v[i].time_t[1], &score_v[i].coin_c[1],
230 &score_v[i].time_t[2], &score_v[i].coin_c[2]);
236 /*---------------------------------------------------------------------------*/
238 const char *level_shot(int i)
240 return level_v[i].shot;
243 const char *level_time_n(int i, int j)
245 return score_v[i].time_n[j];
248 const char *level_coin_n(int i, int j)
250 return score_v[i].coin_n[j];
253 /*---------------------------------------------------------------------------*/
254 /* Return the coin count for the Most Coins or Best Time score. */
256 int level_coin_c(int i, int j)
261 return score_v[i].coin_c[j];
264 int level_time_c(int i, int j)
266 return score_v[i].time_c[j];
269 /*---------------------------------------------------------------------------*/
270 /* Return the time for the Most Coins or Best Time score. */
272 int level_coin_t(int i, int j)
274 return score_v[i].coin_t[j];
277 int level_time_t(int i, int j)
280 return level_v[i].time - curr_clock();
282 return score_v[i].time_t[j];
285 /*---------------------------------------------------------------------------*/
287 void level_init(const char *init_levels,
288 const char *init_scores,
289 const char *user_scores)
291 memset(level_v, 0, sizeof (struct level) * MAXLVL);
292 memset(score_v, 0, sizeof (struct score) * MAXLVL);
294 level_init_rc(init_levels);
295 level_init_hs(init_scores);
296 level_load_hs(user_scores);
298 strncpy(scores_file, user_scores, MAXSTR);
311 level_store_hs(user_scores);
315 void level_free(void)
319 level_store_hs(scores_file);
321 for (i = 0; i < count; i++)
322 if (glIsTexture(level_v[i].text))
323 glDeleteTextures(1, &level_v[i].text);
328 int level_exists(int i)
330 return (0 < i && i < count);
333 int level_opened(int i)
335 return level_exists(i) && (0 < i && i < count && i <= limit);
338 int level_locked(int i)
340 return level_opened(i) && (i == limit) && (level_v[i].goal > 0);
343 /*---------------------------------------------------------------------------*/
345 int curr_times_total(void) { return times_total; }
346 int curr_coins_total(void) { return coins_total; }
348 int curr_count(void) { return count; }
349 int curr_score(void) { return score; }
350 int curr_coins(void) { return coins; }
351 int curr_balls(void) { return balls; }
352 int curr_level(void) { return level; }
353 int curr_goal (void) { return goal; }
355 /*---------------------------------------------------------------------------*/
357 static int score_time_comp(const struct score *S, int i, int j)
359 if (S->time_t[i] < S->time_t[j])
362 if (S->time_t[i] == S->time_t[j] &&
363 S->time_c[i] > S->time_c[j])
369 static int score_coin_comp(const struct score *S, int i, int j)
371 if (S->coin_c[i] > S->coin_c[j])
374 if (S->coin_c[i] == S->coin_c[j] &&
375 S->coin_t[i] < S->coin_t[j])
381 /*---------------------------------------------------------------------------*/
383 static void score_time_swap(struct score *S, int i, int j)
389 strncpy(n, S->time_n[i], MAXNAM);
390 strncpy(S->time_n[i], S->time_n[j], MAXNAM);
391 strncpy(S->time_n[j], n, MAXNAM);
394 S->time_t[i] = S->time_t[j];
398 S->time_c[i] = S->time_c[j];
402 static void score_coin_swap(struct score *S, int i, int j)
408 strncpy(n, S->coin_n[i], MAXNAM);
409 strncpy(S->coin_n[i], S->coin_n[j], MAXNAM);
410 strncpy(S->coin_n[j], n, MAXNAM);
413 S->coin_t[i] = S->coin_t[j];
417 S->coin_c[i] = S->coin_c[j];
421 /*---------------------------------------------------------------------------*/
423 int level_replay(const char *filename)
427 return demo_replay_init(filename, &score, &coins, &balls, &goal);
430 int level_play(const char *filename, int i, int m)
438 if (m == MODE_CHALLENGE)
439 goal = level_v[level].goal;
441 goal = (level == limit) ? level_v[level].goal : 0;
443 return demo_play_init(USER_REPLAY_FILE,
450 goal, score, coins, balls);
453 /*---------------------------------------------------------------------------*/
455 void level_stat(int s)
457 if ((status = s) == GAME_GOAL)
459 coins_total += coins;
463 demo_play_stat(curr_coins(), level_v[level].time - curr_clock());
473 return (level + 1 == count);
476 int level_exit(const char *filename, int next)
478 times_total += level_v[level].time - curr_clock();
480 demo_play_stop(filename);
489 level_store_hs(scores_file);
494 if (mode == MODE_CHALLENGE)
499 /* Load the next level. */
501 if (status && level < count && balls >= 0)
503 if (mode == MODE_CHALLENGE)
504 goal = level_v[level].goal;
506 goal = (level == limit) ? level_v[level].goal : 0;
510 return demo_play_init(USER_REPLAY_FILE,
517 goal, score, coins, balls);
523 int level_sort(int *time_i, int *coin_i)
525 int i, clock = level_v[level].time - curr_clock();
528 config_get_s(CONFIG_PLAYER, player, MAXNAM);
530 /* Insert the time record into the high score list. */
532 strncpy(score_v[level].time_n[3], player, MAXNAM);
533 score_v[level].time_c[3] = coins;
534 score_v[level].time_t[3] = clock;
536 for (i = 2; i >= 0 && score_time_comp(score_v + level, i + 1, i); i--)
538 score_time_swap(score_v + level, i + 1, i);
542 /* Insert the coin record into the high score list. */
544 strncpy(score_v[level].coin_n[3], player, MAXNAM);
545 score_v[level].coin_c[3] = coins;
546 score_v[level].coin_t[3] = clock;
548 for (i = 2; i >= 0 && score_coin_comp(score_v + level, i + 1, i); i--)
550 score_coin_swap(score_v + level, i + 1, i);
554 return (*time_i < 3 || *coin_i < 3);
557 int level_done(int *time_i, int *coin_i)
562 config_get_s(CONFIG_PLAYER, player, MAXNAM);
564 /* Note a global high score. */
566 strncpy(score_v[0].time_n[3], player, MAXNAM);
567 score_v[0].time_c[3] = coins_total;
568 score_v[0].time_t[3] = times_total;
570 strncpy(score_v[0].coin_n[3], player, MAXNAM);
571 score_v[0].coin_c[3] = coins_total;
572 score_v[0].coin_t[3] = times_total;
574 if (level == count && level_total == count - 1)
576 /* Insert the time record into the global high score list. */
578 for (i = 2; i >= 0 && score_time_comp(score_v, i + 1, i); i--)
580 score_time_swap(score_v, i + 1, i);
584 /* Insert the coin record into the global high score list. */
586 for (i = 2; i >= 0 && score_coin_comp(score_v, i + 1, i); i--)
588 score_coin_swap(score_v, i + 1, i);
593 return (*time_i < 3 || *coin_i < 3);
596 int level_score(int n)
598 int sound = AUD_COIN;
603 /* Pulse the coin counter based on the value of the grabbed coin. */
605 if (n >= 10) hud_coin_pulse(2.00f);
606 else if (n >= 5) hud_coin_pulse(1.50f);
607 else hud_coin_pulse(1.25f);
609 /* Check for goal open. */
613 if (n >= 10) hud_goal_pulse(2.00f);
614 else if (n >= 5) hud_goal_pulse(1.50f);
615 else hud_goal_pulse(1.25f);
621 hud_goal_pulse(2.0f);
624 goal = (goal > n) ? (goal - n) : 0;
627 audio_play(sound, 1.f);
631 int level_count(void)
633 if (mode != MODE_CHALLENGE)
640 if (score % 100 == 0)
643 audio_play(AUD_BALL, 1.0f);
650 /*---------------------------------------------------------------------------*/
652 void level_name(int i, const char *name, int time_i, int coin_i)
654 strncpy(score_v[i].time_n[time_i], name, MAXNAM);
655 strncpy(score_v[i].coin_n[coin_i], name, MAXNAM);
658 void level_snap(int i)
660 char filename[MAXSTR];
662 /* Convert the level name to a BMP filename. */
664 memset(filename, 0, MAXSTR);
665 strncpy(filename, level_v[i].file, strcspn(level_v[i].file, "."));
666 strcat(filename, ".bmp");
668 /* Initialize the game for a snapshot. */
670 if (game_init(level_v[i].file, level_v[i].back, level_v[i].grad, 0, 1))
672 /* Render the level and grab the screen. */
678 SDL_GL_SwapBuffers();
680 image_snap(filename);
684 /*---------------------------------------------------------------------------*/