running: tweaks
[neverball] / ball / set.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 <assert.h>
18 #include <errno.h>
19
20 #include "glext.h"
21 #include "config.h"
22 #include "image.h"
23 #include "text.h"
24 #include "set.h"
25 #include "game.h"
26 #include "common.h"
27
28 /*---------------------------------------------------------------------------*/
29
30 struct set
31 {
32     char file[PATHMAX];
33
34     char *id;                  /* Internal set identifier    */
35     char *name;                /* Set name                   */
36     char *desc;                /* Set description            */
37     char *shot;                /* Set screen-shot            */
38
39     char user_scores[PATHMAX]; /* User high-score file       */
40
41     struct score coin_score;   /* Challenge score            */
42     struct score time_score;   /* Challenge score            */
43
44     /* Level info */
45
46     int   count;                /* Number of levels           */
47     char *level_name_v[MAXLVL]; /* List of level file names   */
48 };
49
50 static int set_state = 0;
51
52 static int set;
53 static int count;
54
55 static struct set   set_v[MAXSET];
56 static struct level level_v[MAXLVL];
57
58 /*---------------------------------------------------------------------------*/
59
60 static void put_score(FILE *fp, const struct score *s)
61 {
62     int j;
63
64     for (j = 0; j < NSCORE; j++)
65         fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
66 }
67
68 void set_store_hs(void)
69 {
70     const struct set *s = &set_v[set];
71     FILE *fout;
72     int i;
73     const struct level *l;
74     char states[MAXLVL + 1];
75
76     if ((fout = fopen(config_user(s->user_scores), "w")))
77     {
78         for (i = 0; i < s->count; i++)
79         {
80             if (level_v[i].is_locked)
81                 states[i] = 'L';
82             else if (level_v[i].is_completed)
83                 states[i] = 'C';
84             else
85                 states[i] = 'O';
86         }
87         states[s->count] = '\0';
88         fprintf(fout, "%s\n",states);
89
90         put_score(fout, &s->time_score);
91         put_score(fout, &s->coin_score);
92
93         for (i = 0; i < s->count; i++)
94         {
95             l = &level_v[i];
96
97             put_score(fout, &l->score.best_times);
98             put_score(fout, &l->score.unlock_goal);
99             put_score(fout, &l->score.most_coins);
100         }
101
102         fclose(fout);
103     }
104 }
105
106 static int get_score(FILE *fp, struct score *s)
107 {
108     int j;
109     int res = 1;
110
111     for (j = 0; j < NSCORE && res; j++)
112     {
113         res = fscanf(fp, "%d %d %s\n",
114                      &s->timer[j],
115                      &s->coins[j],
116                      s->player[j]) == 3;
117     }
118     return res;
119 }
120
121 /* Get the score of the set. */
122 static void set_load_hs(void)
123 {
124     struct set *s = &set_v[set];
125     FILE *fin;
126     int i;
127     int res = 0;
128     struct level *l;
129     const char *fn = config_user(s->user_scores);
130     char states[MAXLVL + 1];
131
132     if ((fin = fopen(fn, "r")))
133     {
134         res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
135
136         for (i = 0; i < s->count && res; i++)
137         {
138             switch (states[i])
139             {
140             case 'L':
141                 level_v[i].is_locked = 1;
142                 level_v[i].is_completed = 0;
143                 break;
144
145             case 'C':
146                 level_v[i].is_locked = 0;
147                 level_v[i].is_completed = 1;
148                 break;
149
150             case 'O':
151                 level_v[i].is_locked = 0;
152                 level_v[i].is_completed = 0;
153                 break;
154
155             default:
156                 res = 0;
157             }
158         }
159
160         res = res &&
161             get_score(fin, &s->time_score) &&
162             get_score(fin, &s->coin_score);
163
164         for (i = 0; i < s->count && res; i++)
165         {
166             l = &level_v[i];
167             res = get_score(fin, &l->score.best_times) &&
168                 get_score(fin, &l->score.unlock_goal) &&
169                 get_score(fin, &l->score.most_coins);
170         }
171
172         fclose(fin);
173     }
174
175     if (!res && errno != ENOENT)
176     {
177         fprintf(stderr,
178                 L_("Error while loading user high-score file '%s': %s\n"),
179                 fn, errno ? strerror(errno) : L_("Incorrect format"));
180     }
181 }
182
183 /*---------------------------------------------------------------------------*/
184
185 static int set_load(struct set *s, const char *filename)
186 {
187     FILE *fin;
188     char *scores, *level_name;
189
190     fin = fopen(config_data(filename), "r");
191
192     if (!fin)
193     {
194         fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
195                 filename, strerror(errno));
196         return 0;
197     }
198
199     memset(s, 0, sizeof (struct set));
200
201     /* Set some sane values in case the scores are missing. */
202
203     score_init_hs(&s->time_score, 359999, 0);
204     score_init_hs(&s->coin_score, 359999, 0);
205
206     strncpy(s->file, filename, PATHMAX - 1);
207
208     if (read_line(&s->name, fin) &&
209         read_line(&s->desc, fin) &&
210         read_line(&s->id,   fin) &&
211         read_line(&s->shot, fin) &&
212         read_line(&scores,  fin))
213     {
214         sscanf(scores, "%d %d %d %d %d %d",
215                &s->time_score.timer[0],
216                &s->time_score.timer[1],
217                &s->time_score.timer[2],
218                &s->coin_score.coins[0],
219                &s->coin_score.coins[1],
220                &s->coin_score.coins[2]);
221
222         free(scores);
223
224         strncpy(s->user_scores, "neverballhs-", PATHMAX - 1);
225         strncat(s->user_scores, s->id, PATHMAX - 1 - strlen("neverballhs-"));
226
227         s->count = 0;
228
229         while (s->count < MAXLVL && read_line(&level_name, fin))
230         {
231             s->level_name_v[s->count] = level_name;
232             s->count++;
233         }
234
235         fclose(fin);
236
237         return 1;
238     }
239
240     free(s->name);
241     free(s->desc);
242     free(s->id);
243     free(s->shot);
244
245     fclose(fin);
246
247     return 0;
248 }
249
250 int set_init()
251 {
252     FILE *fin;
253     char *name;
254
255     if (set_state)
256         set_free();
257
258     set   = 0;
259     count = 0;
260
261     if ((fin = fopen(config_data(SET_FILE), "r")))
262     {
263         while (count < MAXSET && read_line(&name, fin))
264         {
265             if (set_load(&set_v[count], name))
266                 count++;
267
268             free(name);
269         }
270         fclose(fin);
271
272         set_state = 1;
273     }
274
275     return count;
276 }
277
278 void set_free(void)
279 {
280     int i, j;
281
282     for (i = 0; i < count; i++)
283     {
284         free(set_v[i].name);
285         free(set_v[i].desc);
286         free(set_v[i].id);
287         free(set_v[i].shot);
288
289         for (j = 0; j < set_v[i].count; j++)
290             free(set_v[i].level_name_v[j]);
291     }
292
293     set_state = 0;
294 }
295
296 /*---------------------------------------------------------------------------*/
297
298 int set_exists(int i)
299 {
300     return (0 <= i && i < count);
301 }
302
303 const char *set_id(int i)
304 {
305     return set_exists(i) ? set_v[i].id : NULL;
306 }
307
308 const char *set_name(int i)
309 {
310     return set_exists(i) ? _(set_v[i].name) : NULL;
311 }
312
313 const char *set_desc(int i)
314 {
315     return set_exists(i) ? _(set_v[i].desc) : NULL;
316 }
317
318 const char *set_shot(int i)
319 {
320     return set_exists(i) ? set_v[i].shot : NULL;
321 }
322
323 const struct score *set_time_score(int i)
324 {
325     return set_exists(i) ? &set_v[i].time_score : NULL;
326 }
327
328 const struct score *set_coin_score(int i)
329 {
330     return set_exists(i) ? &set_v[i].coin_score : NULL;
331 }
332
333 /*---------------------------------------------------------------------------*/
334
335 int set_level_exists(int s, int i)
336 {
337     return (i >= 0 && i < set_v[s].count);
338 }
339
340 static void set_load_levels(void)
341 {
342     struct level *l;
343     int nb = 1, bnb = 1;
344
345     int i;
346
347     const char *roman[] = {
348         "",
349         "I",   "II",   "III",   "IV",   "V",
350         "VI",  "VII",  "VIII",  "IX",   "X",
351         "XI",  "XII",  "XIII",  "XIV",  "XV",
352         "XVI", "XVII", "XVIII", "XIX",  "XX",
353         "XXI", "XXII", "XXIII", "XXIV", "XXV"
354     };
355
356     for (i = 0; i < set_v[set].count; i++)
357     {
358         l = &level_v[i];
359
360         level_load(set_v[set].level_name_v[i], l);
361
362         l->set    = &set_v[set];
363         l->number = i;
364
365         if (l->is_bonus)
366             sprintf(l->name, "%s",   roman[bnb++]);
367         else
368             sprintf(l->name, "%02d", nb++);
369
370         l->is_locked    = 1;
371         l->is_completed = 0;
372     }
373
374     /* Unlock first level. */
375
376     level_v[0].is_locked = 0;
377 }
378
379 void set_goto(int i)
380 {
381     set = i;
382
383     set_load_levels();
384     set_load_hs();
385 }
386
387 int curr_set(void)
388 {
389     return set;
390 }
391
392 struct level *get_level(int i)
393 {
394     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
395 }
396
397 /*---------------------------------------------------------------------------*/
398
399 int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
400 {
401     struct set *s = &set_v[set];
402     char player[MAXSTR] = "";
403
404     config_get_s(CONFIG_PLAYER, player, MAXSTR);
405
406     if (score_rank)
407         *score_rank = score_coin_insert(&s->coin_score, player, timer, coins);
408
409     if (times_rank)
410         *times_rank = score_time_insert(&s->time_score, player, timer, coins);
411
412     if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
413         return 1;
414     else
415         return 0;
416 }
417
418 void set_rename_player(int score_rank, int times_rank, const char *player)
419 {
420     struct set *s = &set_v[set];
421
422     strncpy(s->coin_score.player[score_rank], player, MAXNAM);
423     strncpy(s->time_score.player[times_rank], player, MAXNAM);
424 }
425
426 /*---------------------------------------------------------------------------*/
427
428 void level_snap(int i)
429 {
430     char filename[MAXSTR];
431     char *ext;
432
433     /* Convert the level name to a PNG filename. */
434
435     memset(filename, 0, MAXSTR);
436
437     ext = strrchr(level_v[i].file, '.');
438     strncpy(filename, level_v[i].file,
439             ext ? ext - level_v[i].file : strlen(level_v[i].file));
440     strcat(filename, ".png");
441
442     /* Initialize the game for a snapshot. */
443
444     if (game_init(level_v[i].file, 0, 1))
445     {
446         /* Render the level and grab the screen. */
447
448         config_clear();
449         game_set_fly(1.f);
450         game_kill_fade();
451         game_draw(1, 0);
452
453         image_snap(filename);
454
455         SDL_GL_SwapBuffers();
456     }
457 }
458
459 void set_cheat(void)
460 {
461     int i;
462
463     for (i = 0; i < set_v[set].count; i++)
464     {
465         level_v[i].is_locked    = 0;
466         level_v[i].is_completed = 1;
467     }
468 }
469
470 /*---------------------------------------------------------------------------*/