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