add more feedback: bonus level and set completed
[neverball] / ball / level.c
1 /*   
2  * Copyright (C) 2003 Robert Kooima
3  *
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.
8  *
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.
13  */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <math.h>
18
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"
28
29 /*---------------------------------------------------------------------------*/
30
31 struct score
32 {
33     char time_n[4][MAXNAM];
34     int  time_t[4];
35     int  time_c[4];
36
37     char coin_n[4][MAXNAM];
38     int  coin_t[4];
39     int  coin_c[4];
40 };
41
42 struct level
43 {
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;
51
52     GLuint text;
53 };
54
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         */
59
60 static int level;                       /* Current level number       */
61 static int count;                       /* Number of levels           */
62 static int limit;                       /* Last opened (locked) level */
63
64 static int mode;                        /* Current play mode          */
65
66 static int coins_total;
67 static int times_total;
68
69 static struct level level_v[MAXLVL];
70 static struct score score_v[MAXLVL];
71
72 static char scores_file[MAXSTR];
73
74 /*---------------------------------------------------------------------------*/
75
76 static void level_store_hs(const char *filename)
77 {
78     FILE *fout;
79
80     if ((fout = fopen(config_user(filename), "w")))
81     {
82         int i;
83         int j;
84
85         for (i = 0; i < limit; i++)
86             for (j = 0; j < 3; j++)
87             {
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);
92
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]);
101             }
102             
103         fclose(fout);
104     }
105 }
106
107 static void level_load_hs(const char *filename)
108 {
109     FILE *fin;
110
111     limit = 1;
112
113     if ((fin = fopen(config_user(filename), "r")))
114     {
115         int i;
116
117         for (i = 0; i < count; i++)
118         {
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;
144         }
145
146         fclose(fin);
147     }
148 }
149
150 /*---------------------------------------------------------------------------*/
151
152 static void level_init_rc(const char *filename)
153 {
154     FILE *fin;
155     char buf[MAXSTR];
156
157     count = 0;
158     level = 0;
159     coins = 0;
160     score = 0;
161     balls = 0;
162
163     /* Load the levels list. */
164
165     if ((fin = fopen(config_data(filename), "r")))
166     {
167         while (count < MAXLVL && fgets(buf, MAXSTR, fin))
168         {
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++;
178         }
179         fclose(fin);
180     }
181 }
182
183 static void level_init_hs(const char *filename)
184 {
185     char buf[MAXSTR];
186     FILE *fin;
187     int i = 0;
188
189     /* Set some sane values in case the scores file is missing. */
190
191     for (i = 0; i < MAXLVL; i++)
192     {
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");
196
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;
200
201         score_v[i].time_c[0] = 0;
202         score_v[i].time_c[1] = 0;
203         score_v[i].time_c[2] = 0;
204
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");
208
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;
212
213         score_v[i].coin_c[0] = 0;
214         score_v[i].coin_c[1] = 0;
215         score_v[i].coin_c[2] = 0;
216     }
217
218     /* Load the default high scores file. */
219
220     if ((fin = fopen(config_data(filename), "r")))
221     {
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]);
227
228         fclose(fin);
229     }
230 }
231
232 /*---------------------------------------------------------------------------*/
233
234 const char *level_shot(int i)
235 {
236     return level_v[i].shot;
237 }
238
239 const char *level_time_n(int i, int j)
240 {
241     return score_v[i].time_n[j];
242 }
243
244 const char *level_coin_n(int i, int j)
245 {
246     return score_v[i].coin_n[j];
247 }
248
249 /*---------------------------------------------------------------------------*/
250 /* Return the coin count for the Most Coins or Best Time score.              */
251
252 int level_coin_c(int i, int j)
253 {
254     if (j < 0)
255         return score;
256     else
257         return score_v[i].coin_c[j];
258 }
259
260 int level_time_c(int i, int j)
261 {
262     return score_v[i].time_c[j];
263 }
264
265 /*---------------------------------------------------------------------------*/
266 /* Return the time for the Most Coins or Best Time score.                    */
267
268 int level_coin_t(int i, int j)
269 {
270     return score_v[i].coin_t[j];
271 }
272
273 int level_time_t(int i, int j)
274 {
275     if (j < 0)
276         return level_v[i].time - curr_clock();
277     else
278         return score_v[i].time_t[j];
279 }
280
281 /*---------------------------------------------------------------------------*/
282
283 void level_init(const char *init_levels,
284                 const char *init_scores,
285                 const char *user_scores)
286 {
287     memset(level_v, 0, sizeof (struct level) * MAXLVL);
288     memset(score_v, 0, sizeof (struct score) * MAXLVL);
289
290     level_init_rc(init_levels);
291     level_init_hs(init_scores);
292     level_load_hs(user_scores);
293
294     strncpy(scores_file, user_scores, MAXSTR);
295
296     level = 0;
297
298 }
299
300 void level_cheat(void)
301 /* Open each level of the set */
302 {
303     limit = count;
304 }
305
306 void level_free(void)
307 {
308     int i;
309
310     level_store_hs(scores_file);
311
312     for (i = 0; i < count; i++)
313         if (glIsTexture(level_v[i].text))
314             glDeleteTextures(1, &level_v[i].text);
315
316     count = 0;
317 }
318
319 int level_exists(int i)
320 {
321     return (0 < i && i < count);
322 }
323
324 int level_opened(int i)
325 {
326     return level_exists(i) && (0 < i && i < count && i <= limit);
327 }
328
329 int level_locked(int i)
330 {
331     return level_opened(i) && (i == limit);
332 }
333
334 int level_extra_bonus(int i)
335 {
336     return level_exists(i) && (i > 20);
337 }
338
339 int level_extra_bonus_opened(void)
340 {
341     return level_opened(21);
342 }
343
344 int level_set_completed(void)
345 {
346     return limit >= count;
347 }
348
349 /*---------------------------------------------------------------------------*/
350
351 int curr_times_total(void) { return times_total; }
352 int curr_coins_total(void) { return coins_total; }
353
354 int curr_count(void) { return count; }
355 int curr_score(void) { return score; }
356 int curr_coins(void) { return coins; }
357 int curr_balls(void) { return balls; }
358 int curr_level(void) { return level; }
359 int curr_goal (void) { return goal;  }
360
361 /*---------------------------------------------------------------------------*/
362
363 static int score_time_comp(const struct score *S, int i, int j)
364 {
365     if (S->time_t[i] <  S->time_t[j])
366         return 1;
367
368     if (S->time_t[i] == S->time_t[j] &&
369         S->time_c[i] >  S->time_c[j])
370         return 1;
371
372     return 0;
373 }
374
375 static int score_coin_comp(const struct score *S, int i, int j)
376 {
377     if (S->coin_c[i] >  S->coin_c[j])
378         return 1;
379
380     if (S->coin_c[i] == S->coin_c[j] &&
381         S->coin_t[i] <  S->coin_t[j])
382         return 1;
383
384     return 0;
385 }
386
387 /*---------------------------------------------------------------------------*/
388
389 static void score_time_swap(struct score *S, int i, int j)
390 {
391     char n[MAXNAM];
392     int  t;
393     int  c;
394
395     strncpy(n,            S->time_n[i], MAXNAM);
396     strncpy(S->time_n[i], S->time_n[j], MAXNAM);
397     strncpy(S->time_n[j], n,            MAXNAM);
398
399     t            = S->time_t[i];
400     S->time_t[i] = S->time_t[j];
401     S->time_t[j] = t;
402
403     c            = S->time_c[i];
404     S->time_c[i] = S->time_c[j];
405     S->time_c[j] = c;
406 }
407
408 static void score_coin_swap(struct score *S, int i, int j)
409 {
410     char n[MAXNAM];
411     int  t;
412     int  c;
413
414     strncpy(n,            S->coin_n[i], MAXNAM);
415     strncpy(S->coin_n[i], S->coin_n[j], MAXNAM);
416     strncpy(S->coin_n[j], n,            MAXNAM);
417
418     t            = S->coin_t[i];
419     S->coin_t[i] = S->coin_t[j];
420     S->coin_t[j] = t;
421
422     c            = S->coin_c[i];
423     S->coin_c[i] = S->coin_c[j];
424     S->coin_c[j] = c;
425 }
426
427 /*---------------------------------------------------------------------------*/
428
429 int level_replay(const char *filename)
430 {
431     int r, time;
432
433     r = demo_replay_init(filename, &score, &coins, &balls, &goal, &time);
434
435     mode = (time == 0) ? MODE_PRACTICE : MODE_NORMAL;
436
437     return r;
438 }
439
440 int level_play_go(void)
441 /* Start to play the current level */
442 {
443     int time;
444     
445     coins  = 0;
446     goal   = (mode == MODE_PRACTICE) ? 0 : level_v[level].goal;
447     time   = (mode == MODE_PRACTICE) ? 0 : level_v[level].time;
448     
449     return demo_play_init(USER_REPLAY_FILE,
450                           level_v[level].file,
451                           level_v[level].back,
452                           level_v[level].grad,
453                           level_v[level].song,
454                           level_v[level].shot,
455                           time,
456                           goal, score, coins, balls);
457 }
458
459 void level_play(int i, int m)
460 /* Prepare to play a level sequence from the `i'th level */
461 {
462     mode = m;
463     level = i;
464
465     score = 0;
466     balls = 3;
467     coins_total = 0;
468     times_total = 0;
469 }
470
471 /*---------------------------------------------------------------------------*/
472
473 void level_stop(int state)
474 /* Stop the current playing level */
475 {
476     int time;
477     
478     /* open next level */
479     if (state == GAME_GOAL && mode != MODE_PRACTICE && limit < level+1)
480         if (level_extra_bonus_opened() || !level_extra_bonus(level+1) || mode == MODE_CHALLENGE)
481             limit = level + 1;
482     
483     if (mode == MODE_CHALLENGE)
484     {
485         /* sum time */
486         times_total += level_v[level].time - curr_clock(); 
487             
488         /* sum coins */
489         if (state == GAME_GOAL)
490             coins_total += coins;
491
492         /* lose ball */
493         if (state == GAME_TIME || state == GAME_FALL)
494             balls--;
495     }
496
497     /* stop demo recording */   
498     time = (mode == MODE_PRACTICE) ? curr_clock() : level_v[level].time - curr_clock();
499     demo_play_stop(curr_coins(), time);
500 }
501
502 int level_dead(void)
503 {
504     return (mode == MODE_CHALLENGE) && (balls <= 0);
505 }
506
507 int level_last(void)
508 {
509     return (level + 1 == count) || (level_extra_bonus(level + 1));
510 }
511
512 void level_next(void)
513 {
514     level++;
515 }
516
517 int level_sort(int *time_i, int *coin_i)
518 {
519     int i, clock;
520     char player[MAXNAM];
521      
522     if (mode == MODE_PRACTICE)
523         clock = curr_clock();
524     else
525         clock = level_v[level].time - curr_clock();
526
527     config_get_s(CONFIG_PLAYER, player, MAXNAM);
528
529     /* Insert the time record into the high score list. */
530
531     strncpy(score_v[level].time_n[3], player, MAXNAM);
532     score_v[level].time_c[3] = coins;
533     score_v[level].time_t[3] = clock;
534
535     for (i = 2; i >= 0 && score_time_comp(score_v + level, i + 1, i); i--)
536     {
537         score_time_swap(score_v + level, i + 1, i);
538         *time_i = i;
539     }
540
541     /* Insert the coin record into the high score list. */
542
543     strncpy(score_v[level].coin_n[3], player, MAXNAM);
544     score_v[level].coin_c[3] = coins;
545     score_v[level].coin_t[3] = clock;
546
547     for (i = 2; i >= 0 && score_coin_comp(score_v + level, i + 1, i); i--)
548     {
549         score_coin_swap(score_v + level, i + 1, i);
550         *coin_i = i;
551     }
552
553     return (*time_i < 3 || *coin_i < 3);
554 }
555
556 int level_done(int *time_i, int *coin_i)
557 {
558     int i;
559     char player[MAXNAM];
560
561     config_get_s(CONFIG_PLAYER, player, MAXNAM);
562
563     /* Note a global high score. */
564
565     strncpy(score_v[0].time_n[3], player, MAXNAM);
566     score_v[0].time_c[3] = coins_total;
567     score_v[0].time_t[3] = times_total;
568
569     strncpy(score_v[0].coin_n[3], player, MAXNAM);
570     score_v[0].coin_c[3] = coins_total;
571     score_v[0].coin_t[3] = times_total;
572
573     /* Insert the time record into the global high score list. */
574
575     for (i = 2; i >= 0 && score_time_comp(score_v, i + 1, i); i--)
576     {
577         score_time_swap(score_v, i + 1, i);
578         *time_i = i;
579     }
580
581     /* Insert the coin record into the global high score list. */
582
583     for (i = 2; i >= 0 && score_coin_comp(score_v, i + 1, i); i--)
584     {
585         score_coin_swap(score_v, i + 1, i);
586         *coin_i = i;
587     }
588
589     return (*time_i < 3 || *coin_i < 3);
590 }
591
592 int level_score(int n)
593 {
594     int sound = AUD_COIN;
595     int value = 0;
596
597     coins += n;
598
599     /* Pulse the coin counter based on the value of the grabbed coin. */
600
601     if      (n >= 10) hud_coin_pulse(2.00f);
602     else if (n >=  5) hud_coin_pulse(1.50f);
603     else              hud_coin_pulse(1.25f);
604
605     /* Check for goal open. */
606
607     if (goal > 0)
608     {
609         if      (n >= 10) hud_goal_pulse(2.00f);
610         else if (n >=  5) hud_goal_pulse(1.50f);
611         else              hud_goal_pulse(1.25f);
612
613         if (goal - n <= 0)
614         {
615             sound = AUD_SWITCH;
616             value = 1;
617             hud_goal_pulse(2.0f);
618         }
619
620         goal = (goal > n) ? (goal - n) : 0;
621     }
622
623     audio_play(sound, 1.f);
624     return value;
625 }
626
627 int level_count(void)
628 {
629     if (mode != MODE_CHALLENGE)
630         return 0;
631     if (coins > 0)
632     {
633         score++;
634         coins--;
635
636         if (score % 100 == 0)
637         {
638             balls += 1;
639             audio_play(AUD_BALL, 1.0f);
640         }
641         return 1;
642     }
643     return 0;
644 }
645
646 /*---------------------------------------------------------------------------*/
647
648 void level_name(int i, const char *name, int time_i, int coin_i)
649 {
650     strncpy(score_v[i].time_n[time_i], name, MAXNAM);
651     strncpy(score_v[i].coin_n[coin_i], name, MAXNAM);
652 }
653
654 void level_snap(int i)
655 {
656     char filename[MAXSTR];
657
658     /* Convert the level name to a BMP filename. */
659
660     memset(filename, 0, MAXSTR);
661     strncpy(filename, level_v[i].file, strcspn(level_v[i].file, "."));
662     strcat(filename, ".bmp");
663
664     /* Initialize the game for a snapshot. */
665
666     if (game_init(level_v[i].file, level_v[i].back, level_v[i].grad, 0, 1))
667     {
668         /* Render the level and grab the screen. */
669
670         config_clear();
671         game_set_fly(1.f);
672         game_kill_fade();
673         game_draw(1, 0);
674         SDL_GL_SwapBuffers();
675
676         image_snap(filename);
677     }
678 }
679
680 /*---------------------------------------------------------------------------*/
681
682 int level_mode(void)
683 {
684     return mode;
685 }
686