Import from neverball-1.3.1.tar.gz
[neverball-archive.git] / ball / level.c
blob06abe003eaa7f73675478f348f926531c69215e9
1 /*
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.
15 #include <stdio.h>
16 #include <string.h>
17 #include <math.h>
19 #include "level.h"
20 #include "glext.h"
21 #include "image.h"
22 #include "game.h"
23 #include "geom.h"
24 #include "demo.h"
25 #include "hud.h"
26 #include "audio.h"
27 #include "config.h"
29 /*---------------------------------------------------------------------------*/
31 struct score
33 char time_n[4][MAXNAM];
34 int time_t[4];
35 int time_c[4];
37 char coin_n[4][MAXNAM];
38 int coin_t[4];
39 int coin_c[4];
42 struct level
44 char file[MAXSTR];
45 char back[MAXSTR];
46 char grad[MAXSTR];
47 char shot[MAXSTR];
48 char song[MAXSTR];
49 int time;
50 int goal;
52 GLuint text;
55 static int score; /* Current coin total */
56 static int coins; /* Current coin count */
57 static int balls; /* Current life count */
58 static int goal; /* Current goal count */
60 static int level; /* Current level number */
61 static int count; /* Number of levels */
62 static int limit; /* Last opened (locked) level */
63 static int status; /* Status of current level */
65 static int level_total;
66 static int coins_total;
67 static int times_total;
69 static struct level level_v[MAXLVL];
70 static struct score score_v[MAXLVL];
72 static char scores_file[MAXSTR];
74 /*---------------------------------------------------------------------------*/
76 static void level_store_hs(const char *filename)
78 FILE *fout;
80 if ((fout = fopen(config_user(filename), "w")))
82 int i;
83 int j;
85 for (i = 0; i < limit; i++)
86 for (j = 0; j < 3; j++)
88 if (strlen(score_v[i].time_n[j]) == 0)
89 strcpy(score_v[i].time_n[j], DEFAULT_PLAYER);
90 if (strlen(score_v[i].coin_n[j]) == 0)
91 strcpy(score_v[i].coin_n[j], DEFAULT_PLAYER);
93 fprintf(fout, "%d %d %s\n",
94 score_v[i].time_t[j],
95 score_v[i].time_c[j],
96 score_v[i].time_n[j]);
97 fprintf(fout, "%d %d %s\n",
98 score_v[i].coin_t[j],
99 score_v[i].coin_c[j],
100 score_v[i].coin_n[j]);
103 fclose(fout);
107 static void level_load_hs(const char *filename)
109 FILE *fin;
111 limit = 1;
113 if ((fin = fopen(config_user(filename), "r")))
115 int i;
117 for (i = 0; i < count; i++)
119 if (fscanf(fin, "%d %d %s",
120 &score_v[i].time_t[0],
121 &score_v[i].time_c[0],
122 score_v[i].time_n[0]) == 3 &&
123 fscanf(fin, "%d %d %s",
124 &score_v[i].coin_t[0],
125 &score_v[i].coin_c[0],
126 score_v[i].coin_n[0]) == 3 &&
127 fscanf(fin, "%d %d %s",
128 &score_v[i].time_t[1],
129 &score_v[i].time_c[1],
130 score_v[i].time_n[1]) == 3 &&
131 fscanf(fin, "%d %d %s",
132 &score_v[i].coin_t[1],
133 &score_v[i].coin_c[1],
134 score_v[i].coin_n[1]) == 3 &&
135 fscanf(fin, "%d %d %s",
136 &score_v[i].time_t[2],
137 &score_v[i].time_c[2],
138 score_v[i].time_n[2]) == 3 &&
139 fscanf(fin, "%d %d %s",
140 &score_v[i].coin_t[2],
141 &score_v[i].coin_c[2],
142 score_v[i].coin_n[2]) == 3)
143 limit = i + 1;
146 fclose(fin);
150 /*---------------------------------------------------------------------------*/
152 static void level_init_rc(const char *filename)
154 FILE *fin;
155 char buf[MAXSTR];
157 count = 0;
158 level = 0;
159 coins = 0;
160 score = 0;
161 balls = 0;
163 /* Load the levels list. */
165 if ((fin = fopen(config_data(filename), "r")))
167 while (count < MAXLVL && fgets(buf, MAXSTR, fin))
169 sscanf(buf, "%s %s %s %s %d %d %s",
170 level_v[count].file,
171 level_v[count].back,
172 level_v[count].shot,
173 level_v[count].grad,
174 &level_v[count].time,
175 &level_v[count].goal,
176 level_v[count].song);
177 count++;
179 fclose(fin);
183 static void level_init_hs(const char *filename)
185 char buf[MAXSTR];
186 FILE *fin;
187 int i = 0;
189 /* Set some sane values in case the scores file is missing. */
191 for (i = 0; i < MAXLVL; i++)
193 strcpy(score_v[i].time_n[0], "Hard");
194 strcpy(score_v[i].time_n[1], "Medium");
195 strcpy(score_v[i].time_n[2], "Easy");
197 score_v[i].time_t[0] = i ? 59999 : 359999;
198 score_v[i].time_t[1] = i ? 59999 : 359999;
199 score_v[i].time_t[2] = i ? 59999 : 359999;
201 score_v[i].time_c[0] = 0;
202 score_v[i].time_c[1] = 0;
203 score_v[i].time_c[2] = 0;
205 strcpy(score_v[i].coin_n[0], "Hard");
206 strcpy(score_v[i].coin_n[1], "Medium");
207 strcpy(score_v[i].coin_n[2], "Easy");
209 score_v[i].coin_t[0] = i ? 59999 : 359999;
210 score_v[i].coin_t[1] = i ? 59999 : 359999;
211 score_v[i].coin_t[2] = i ? 59999 : 359999;
213 score_v[i].coin_c[0] = 0;
214 score_v[i].coin_c[1] = 0;
215 score_v[i].coin_c[2] = 0;
218 /* Load the default high scores file. */
220 if ((fin = fopen(config_data(filename), "r")))
222 for (i = 0; i < MAXLVL && fgets(buf, MAXSTR, fin); i++)
223 sscanf(buf, "%d %d %d %d %d %d",
224 &score_v[i].time_t[0], &score_v[i].coin_c[0],
225 &score_v[i].time_t[1], &score_v[i].coin_c[1],
226 &score_v[i].time_t[2], &score_v[i].coin_c[2]);
228 fclose(fin);
232 /*---------------------------------------------------------------------------*/
234 const char *level_shot(int i)
236 return level_v[i].shot;
239 const char *level_time_n(int i, int j)
241 return score_v[i].time_n[j];
244 const char *level_coin_n(int i, int j)
246 return score_v[i].coin_n[j];
249 /*---------------------------------------------------------------------------*/
250 /* Return the coin count for the Most Coins or Best Time score. */
252 int level_coin_c(int i, int j)
254 if (j < 0)
255 return score;
256 else
257 return score_v[i].coin_c[j];
260 int level_time_c(int i, int j)
262 return score_v[i].time_c[j];
265 /*---------------------------------------------------------------------------*/
266 /* Return the time for the Most Coins or Best Time score. */
268 int level_coin_t(int i, int j)
270 return score_v[i].coin_t[j];
273 int level_time_t(int i, int j)
275 if (j < 0)
276 return level_v[i].time - curr_clock();
277 else
278 return score_v[i].time_t[j];
281 /*---------------------------------------------------------------------------*/
283 void level_init(const char *init_levels,
284 const char *init_scores,
285 const char *user_scores)
287 memset(level_v, 0, sizeof (struct level) * MAXLVL);
288 memset(score_v, 0, sizeof (struct score) * MAXLVL);
290 level_init_rc(init_levels);
291 level_init_hs(init_scores);
292 level_load_hs(user_scores);
294 strncpy(scores_file, user_scores, MAXSTR);
296 score = 0;
297 coins = 0;
298 balls = 2;
299 level = 0;
301 level_total = 0;
302 coins_total = 0;
303 times_total = 0;
305 #ifdef CHEATER
306 limit = count;
307 level_store_hs(user_scores);
308 #endif
311 void level_free(void)
313 int i;
315 level_store_hs(scores_file);
317 for (i = 0; i < count; i++)
318 if (glIsTexture(level_v[i].text))
319 glDeleteTextures(1, &level_v[i].text);
321 count = 0;
324 int level_exists(int i)
326 return (0 < i && i < count);
329 int level_opened(int i)
331 return level_exists(i) && (0 < i && i < count && i <= limit);
334 int level_locked(int i)
336 return level_opened(i) && (i == limit) && (level_v[i].goal > 0);
339 /*---------------------------------------------------------------------------*/
341 int curr_times_total(void) { return times_total; }
342 int curr_coins_total(void) { return coins_total; }
344 int curr_count(void) { return count; }
345 int curr_score(void) { return score; }
346 int curr_coins(void) { return coins; }
347 int curr_balls(void) { return balls; }
348 int curr_level(void) { return level; }
349 int curr_goal (void) { return goal; }
351 /*---------------------------------------------------------------------------*/
353 static int score_time_comp(const struct score *S, int i, int j)
355 if (S->time_t[i] < S->time_t[j])
356 return 1;
358 if (S->time_t[i] == S->time_t[j] &&
359 S->time_c[i] > S->time_c[j])
360 return 1;
362 return 0;
365 static int score_coin_comp(const struct score *S, int i, int j)
367 if (S->coin_c[i] > S->coin_c[j])
368 return 1;
370 if (S->coin_c[i] == S->coin_c[j] &&
371 S->coin_t[i] < S->coin_t[j])
372 return 1;
374 return 0;
377 /*---------------------------------------------------------------------------*/
379 static void score_time_swap(struct score *S, int i, int j)
381 char n[MAXNAM];
382 int t;
383 int c;
385 strncpy(n, S->time_n[i], MAXNAM);
386 strncpy(S->time_n[i], S->time_n[j], MAXNAM);
387 strncpy(S->time_n[j], n, MAXNAM);
389 t = S->time_t[i];
390 S->time_t[i] = S->time_t[j];
391 S->time_t[j] = t;
393 c = S->time_c[i];
394 S->time_c[i] = S->time_c[j];
395 S->time_c[j] = c;
398 static void score_coin_swap(struct score *S, int i, int j)
400 char n[MAXNAM];
401 int t;
402 int c;
404 strncpy(n, S->coin_n[i], MAXNAM);
405 strncpy(S->coin_n[i], S->coin_n[j], MAXNAM);
406 strncpy(S->coin_n[j], n, MAXNAM);
408 t = S->coin_t[i];
409 S->coin_t[i] = S->coin_t[j];
410 S->coin_t[j] = t;
412 c = S->coin_c[i];
413 S->coin_c[i] = S->coin_c[j];
414 S->coin_c[j] = c;
417 /*---------------------------------------------------------------------------*/
419 int level_replay(const char *filename)
421 status = GAME_NONE;
423 return demo_replay_init(filename, &score, &coins, &balls, &goal);
426 int level_play(const char *filename, int i)
428 status = GAME_NONE;
430 if (i >= 0)
432 level = i;
433 goal = (level == limit) ? level_v[level].goal : 0;
435 return demo_play_init(USER_REPLAY_FILE,
436 level_v[level].file,
437 level_v[level].back,
438 level_v[level].grad,
439 level_v[level].song,
440 level_v[level].shot,
441 level_v[level].time,
442 goal, score, coins, balls);
445 /*---------------------------------------------------------------------------*/
447 void level_stat(int s)
449 if ((status = s) == GAME_GOAL)
451 coins_total += coins;
452 level_total += 1;
455 demo_play_stat(curr_coins(), level_v[level].time - curr_clock());
458 int level_dead(void)
460 return (balls == 0);
463 int level_last(void)
465 return (level + 1 == count);
468 int level_exit(const char *filename, int next)
470 times_total += level_v[level].time - curr_clock();
472 demo_play_stop(filename);
474 switch (status)
476 case GAME_GOAL:
477 if (next) level++;
478 if (limit < level)
479 limit = level;
481 level_store_hs(scores_file);
482 break;
484 case GAME_TIME:
485 case GAME_FALL:
486 balls--;
487 break;
490 /* Load the next level. */
492 if (status && level < count && balls >= 0)
494 goal = (level == limit) ? level_v[level].goal : 0;
495 coins = 0;
496 status = GAME_NONE;
498 return demo_play_init(USER_REPLAY_FILE,
499 level_v[level].file,
500 level_v[level].back,
501 level_v[level].grad,
502 level_v[level].song,
503 level_v[level].shot,
504 level_v[level].time,
505 goal, score, coins, balls);
508 return 0;
511 int level_sort(int *time_i, int *coin_i)
513 int i, clock = level_v[level].time - curr_clock();
514 char player[MAXNAM];
516 config_get_s(CONFIG_PLAYER, player, MAXNAM);
518 /* Insert the time record into the high score list. */
520 strncpy(score_v[level].time_n[3], player, MAXNAM);
521 score_v[level].time_c[3] = coins;
522 score_v[level].time_t[3] = clock;
524 for (i = 2; i >= 0 && score_time_comp(score_v + level, i + 1, i); i--)
526 score_time_swap(score_v + level, i + 1, i);
527 *time_i = i;
530 /* Insert the coin record into the high score list. */
532 strncpy(score_v[level].coin_n[3], player, MAXNAM);
533 score_v[level].coin_c[3] = coins;
534 score_v[level].coin_t[3] = clock;
536 for (i = 2; i >= 0 && score_coin_comp(score_v + level, i + 1, i); i--)
538 score_coin_swap(score_v + level, i + 1, i);
539 *coin_i = i;
542 return (*time_i < 3 || *coin_i < 3);
545 int level_done(int *time_i, int *coin_i)
547 int i;
548 char player[MAXNAM];
550 config_get_s(CONFIG_PLAYER, player, MAXNAM);
552 /* Note a global high score. */
554 strncpy(score_v[0].time_n[3], player, MAXNAM);
555 score_v[0].time_c[3] = coins_total;
556 score_v[0].time_t[3] = times_total;
558 strncpy(score_v[0].coin_n[3], player, MAXNAM);
559 score_v[0].coin_c[3] = coins_total;
560 score_v[0].coin_t[3] = times_total;
562 if (level == count && level_total == count - 1)
564 /* Insert the time record into the global high score list. */
566 for (i = 2; i >= 0 && score_time_comp(score_v, i + 1, i); i--)
568 score_time_swap(score_v, i + 1, i);
569 *time_i = i;
572 /* Insert the coin record into the global high score list. */
574 for (i = 2; i >= 0 && score_coin_comp(score_v, i + 1, i); i--)
576 score_coin_swap(score_v, i + 1, i);
577 *coin_i = i;
581 return (*time_i < 3 || *coin_i < 3);
584 int level_score(int n)
586 int sound = AUD_COIN;
587 int value = 0;
589 coins += n;
591 /* Pulse the coin counter based on the value of the grabbed coin. */
593 if (n >= 10) hud_coin_pulse(2.00f);
594 else if (n >= 5) hud_coin_pulse(1.50f);
595 else hud_coin_pulse(1.25f);
597 /* Check for goal open. */
599 if (goal > 0)
601 if (n >= 10) hud_goal_pulse(2.00f);
602 else if (n >= 5) hud_goal_pulse(1.50f);
603 else hud_goal_pulse(1.25f);
605 if (goal - n <= 0)
607 sound = AUD_SWITCH;
608 value = 1;
609 hud_goal_pulse(2.0f);
612 goal = (goal > n) ? (goal - n) : 0;
615 audio_play(sound, 1.f);
616 return value;
619 int level_count(void)
621 if (coins > 0)
623 score++;
624 coins--;
626 if (score % 100 == 0)
628 balls += 1;
629 audio_play(AUD_BALL, 1.0f);
631 return 1;
633 return 0;
636 /*---------------------------------------------------------------------------*/
638 void level_name(int i, const char *name, int time_i, int coin_i)
640 strncpy(score_v[i].time_n[time_i], name, MAXNAM);
641 strncpy(score_v[i].coin_n[coin_i], name, MAXNAM);
644 void level_snap(int i)
646 char filename[MAXSTR];
648 /* Convert the level name to a BMP filename. */
650 memset(filename, 0, MAXSTR);
651 strncpy(filename, level_v[i].file, strcspn(level_v[i].file, "."));
652 strcat(filename, ".bmp");
654 /* Initialize the game for a snapshot. */
656 if (game_init(level_v[i].file, level_v[i].back, level_v[i].grad, 0, 1))
658 /* Render the level and grab the screen. */
660 config_clear();
661 game_set_fly(1.f);
662 game_kill_fade();
663 game_draw(1, 0);
664 SDL_GL_SwapBuffers();
666 image_snap(filename);