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