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