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