Correct logic of BSP back/front tests
[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 <stdlib.h>
17 #include <string.h>
18 #include <math.h>
19 #include <errno.h>
20 #include <assert.h>
21
22 #include "solid.h"
23 #include "config.h"
24 #include "level.h"
25 #include "set.h"
26
27 /*---------------------------------------------------------------------------*/
28
29 static void scan_level_attribs(struct level *l, const struct s_file *fp)
30 {
31     int i;
32
33     int have_goal = 0, have_time = 0;
34     int need_bt_easy = 0, need_ug_easy = 0, need_mc_easy = 0;
35
36     for (i = 0; i < fp->dc; i++)
37     {
38         char *k = fp->av + fp->dv[i].ai;
39         char *v = fp->av + fp->dv[i].aj;
40
41         if (strcmp(k, "message") == 0)
42             strncpy(l->message, v, MAXSTR);
43         else if (strcmp(k, "song") == 0)
44             strncpy(l->song, v, PATHMAX);
45         else if (strcmp(k, "shot") == 0)
46             strncpy(l->shot, v, PATHMAX);
47         else if (strcmp(k, "goal") == 0)
48         {
49             l->goal = atoi(v);
50             have_goal = 1;
51         }
52         else if (strcmp(k, "time") == 0)
53         {
54             l->time = atoi(v);
55             have_time = 1;
56         }
57         else if (strcmp(k, "time_hs") == 0)
58         {
59             switch (sscanf(v, "%d %d %d",
60                            &l->score.best_times.timer[0],
61                            &l->score.best_times.timer[1],
62                            &l->score.best_times.timer[2]))
63             {
64             case 2: need_bt_easy = 1; break;
65             case 3:                   break;
66
67             default:
68                 /* TODO, complain loudly? */
69                 break;
70             }
71         }
72         else if (strcmp(k, "goal_hs") == 0)
73         {
74             switch (sscanf(v, "%d %d %d",
75                            &l->score.unlock_goal.timer[0],
76                            &l->score.unlock_goal.timer[1],
77                            &l->score.unlock_goal.timer[2]))
78             {
79             case 2: need_ug_easy = 1; break;
80             case 3:                   break;
81
82             default:
83                 /* TODO, complain loudly? */
84                 break;
85             }
86         }
87         else if (strcmp(k, "coin_hs") == 0)
88         {
89             switch (sscanf(v, "%d %d %d",
90                            &l->score.most_coins.coins[0],
91                            &l->score.most_coins.coins[1],
92                            &l->score.most_coins.coins[2]))
93             {
94             case 2: need_mc_easy = 1; break;
95             case 3:                   break;
96
97             default:
98                 /* TODO, complain loudly? */
99                 break;
100             }
101         }
102         else if (strcmp(k, "version") == 0)
103             strncpy(l->version, v, MAXSTR);
104         else if (strcmp(k, "author") == 0)
105             strncpy(l->author, v, MAXSTR);
106         else if (strcmp(k, "bonus") == 0)
107             l->is_bonus = atoi(v) ? 1 : 0;
108     }
109
110     if (have_goal && need_mc_easy)
111         l->score.most_coins.coins[2] = l->goal;
112
113     if (have_time)
114     {
115         if (need_bt_easy)
116             l->score.best_times.timer[2] = l->time;
117         if (need_ug_easy)
118             l->score.unlock_goal.timer[2] = l->time;
119     }
120 }
121
122 int level_load(const char *filename, struct level *level)
123 {
124     struct s_file sol;
125
126     int money;
127     int i;
128
129     memset(level, 0, sizeof (struct level));
130     memset(&sol,  0, sizeof (sol));
131
132 #define format \
133     L_("Error while loading level file '%s': %s\n")
134 #define default_error \
135     L_("Not a valid level file")
136
137     if (!sol_load_only_head(&sol, config_data(filename)))
138     {
139         const char *error = errno ? strerror(errno) : default_error;
140         fprintf(stderr, format, filename, error);
141         return 0;
142     }
143
144 #undef format
145 #undef default_error
146
147     strncpy(level->file, filename, PATHMAX - 1);
148
149     score_init_hs(&level->score.best_times, 59999, 0);
150     score_init_hs(&level->score.unlock_goal, 59999, 0);
151     score_init_hs(&level->score.most_coins, 59999, 0);
152
153     money = 0;
154
155     for (i = 0; i < sol.hc; i++)
156         if (sol.hv[i].t == ITEM_COIN)
157             money += sol.hv[i].n;
158
159     level->score.most_coins.coins[0] = money;
160
161     scan_level_attribs(level, &sol);
162
163     /* Compute initial hs default values */
164
165 #define HOP(t, c) \
166     if (t[2] c t[0]) \
167         t[0] = t[1] = t[2]; \
168     else if (t[2] c t[1]) \
169         t[1] = (t[0] + t[2]) / 2
170
171     HOP(level->score.best_times.timer, <=);
172     HOP(level->score.unlock_goal.timer, <=);
173     HOP(level->score.most_coins.coins, >=);
174
175     sol_free(&sol);
176
177     return 1;
178 }
179
180 void level_dump(const struct level *l)
181 {
182     printf("filename:        %s\n"
183            "version:         %s\n"
184            "author:          %s\n"
185            "time limit:      %d\n"
186            "goal count:      %d\n"
187            "time hs:         %d %d %d\n"
188            "goal hs:         %d %d %d\n"
189            "coin hs:         %d %d %d\n"
190            "message:         %s\n"
191            "screenshot:      %s\n"
192            "song:            %s\n",
193            l->file,
194            l->version,
195            l->author,
196            l->time,
197            l->goal,
198            l->score.best_times.timer[0],
199            l->score.best_times.timer[1],
200            l->score.best_times.timer[2],
201            l->score.unlock_goal.timer[0],
202            l->score.unlock_goal.timer[1],
203            l->score.unlock_goal.timer[2],
204            l->score.most_coins.coins[0],
205            l->score.most_coins.coins[1],
206            l->score.most_coins.coins[2],
207            l->message,
208            l->shot,
209            l->song);
210 }
211
212 /*---------------------------------------------------------------------------*/
213
214 int  level_exists(int i)
215 {
216     return set_level_exists(curr_set(), i);
217 }
218
219 void level_open(int i)
220 {
221     if (level_exists(i))
222         get_level(i)->is_locked = 0;
223 }
224
225 int  level_opened(int i)
226 {
227     return level_exists(i) && !get_level(i)->is_locked;
228 }
229
230 void level_complete(int i)
231 {
232     if (level_exists(i))
233         get_level(i)->is_completed = 1;
234 }
235
236 int  level_completed(int i)
237 {
238     return level_exists(i) && get_level(i)->is_completed;
239 }
240
241 int  level_time (int i)
242 {
243     assert(level_exists(i));
244     return get_level(i)->time;
245 }
246
247 int  level_goal (int i)
248 {
249     assert(level_exists(i));
250     return get_level(i)->goal;
251 }
252
253 int  level_bonus(int i)
254 {
255     return level_exists(i) && get_level(i)->is_bonus;
256 }
257
258 const char *level_shot(int i)
259 {
260     return level_exists(i) ? get_level(i)->shot : NULL;
261 }
262
263 const char *level_file(int i)
264 {
265     return level_exists(i) ? get_level(i)->file : NULL;
266 }
267
268 const char *level_name(int i)
269 {
270     return level_exists(i) ? get_level(i)->name : NULL;
271 }
272
273 const char *level_msg(int i)
274 {
275     if (level_exists(i) && strlen(get_level(i)->message) > 0)
276         return _(get_level(i)->message);
277
278     return NULL;
279 }
280
281 /*---------------------------------------------------------------------------*/
282
283 int level_score_update(int level,
284                        int timer,
285                        int coins,
286                        int *time_rank,
287                        int *goal_rank,
288                        int *coin_rank)
289 {
290     struct level *l = get_level(level);
291     char player[MAXSTR] = "";
292
293     config_get_s(CONFIG_PLAYER, player, MAXSTR);
294
295     if (time_rank)
296         *time_rank = score_time_insert(&l->score.best_times,
297                                        player, timer, coins);
298
299     if (goal_rank)
300         *goal_rank = score_time_insert(&l->score.unlock_goal,
301                                        player, timer, coins);
302
303     if (coin_rank)
304         *coin_rank = score_coin_insert(&l->score.most_coins,
305                                        player, timer, coins);
306
307     if ((time_rank && *time_rank < 3) ||
308         (goal_rank && *goal_rank < 3) ||
309         (coin_rank && *coin_rank < 3))
310         return 1;
311     else
312         return 0;
313 }
314
315 void level_rename_player(int level,
316                          int time_rank,
317                          int goal_rank,
318                          int coin_rank,
319                          const char *player)
320 {
321     struct level *l = get_level(level);
322
323     strncpy(l->score.best_times.player [time_rank], player, MAXNAM);
324     strncpy(l->score.unlock_goal.player[goal_rank], player, MAXNAM);
325     strncpy(l->score.most_coins.player [coin_rank], player, MAXNAM);
326 }
327
328 /*---------------------------------------------------------------------------*/
329