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