Fixed the problem that in a rare case, a set is downloaded and the neverballrc is...
[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
21 #include "config.h"
22 #include "demo.h"
23 #include "text.h"
24 #include "level.h"
25 #include "mode.h"
26 #include "set.h"
27 #include "solid.h"
28
29 /*---------------------------------------------------------------------------*/
30
31 static void scan_dict(struct level *l, const struct s_file *fp)
32 {
33     int i;
34
35     for (i = 0; i < fp->dc; i++)
36     {
37         char *k = fp->av + fp->dv[i].ai;
38         char *v = fp->av + fp->dv[i].aj;
39
40         if (strcmp(k, "message") == 0)
41             strncpy(l->message, v, MAXSTR);
42         else if (strcmp(k, "back") == 0)
43             strncpy(l->back, v, PATHMAX);
44         else if (strcmp(k, "song") == 0)
45             strncpy(l->song, v, PATHMAX);
46         else if (strcmp(k, "grad") == 0)
47             strncpy(l->grad, v, PATHMAX);
48         else if (strcmp(k, "shot") == 0)
49             strncpy(l->shot, v, PATHMAX);
50         else if (strcmp(k, "goal") == 0)
51         {
52             l->goal = atoi(v);
53             l->score.most_coins.coins[2] = l->goal;
54         }
55         else if (strcmp(k, "time") == 0)
56         {
57             l->time = atoi(v);
58             l->score.best_times.timer[2] = l->time;
59             l->score.unlock_goal.timer[2] = l->time;
60         }
61         else if (strcmp(k, "time_hs") == 0)
62             sscanf(v, "%d %d",
63                    &l->score.best_times.timer[0],
64                    &l->score.best_times.timer[1]);
65         else if (strcmp(k, "goal_hs") == 0)
66             sscanf(v, "%d %d",
67                    &l->score.unlock_goal.timer[0],
68                    &l->score.unlock_goal.timer[1]);
69         else if (strcmp(k, "coin_hs") == 0)
70             sscanf(v, "%d %d",
71                    &l->score.most_coins.coins[0],
72                    &l->score.most_coins.coins[1]);
73         else if (strcmp(k, "version") == 0)
74             strncpy(l->version, v, MAXSTR);
75         else if (strcmp(k, "author") == 0)
76             strncpy(l->author, v, MAXSTR);
77         else if (strcmp(k, "bonus") == 0)
78             l->is_bonus = atoi(v) ? 1 : 0;
79     }
80 }
81
82 int level_load(const char *filename, struct level *level)
83 {
84     struct s_file sol;
85
86     int money;
87     int i;
88
89     memset(level, 0, sizeof (struct level));
90     memset(&sol,  0, sizeof (sol));
91
92 #define format \
93     L_("Error while loading level file '%s': %s\n")
94 #define default_error \
95     L_("Not a valid level file")
96
97     if (!sol_load_only_head(&sol, config_data(filename)))
98     {
99         const char *error = errno ? strerror(errno) : default_error;
100         fprintf(stderr, format, filename, error);
101         return 0;
102     }
103
104 #undef format
105 #undef default_error
106
107     strncpy(level->file, filename, PATHMAX - 1);
108
109     score_init_hs(&level->score.best_times, 59999, 0);
110     score_init_hs(&level->score.unlock_goal, 59999, 0);
111     score_init_hs(&level->score.most_coins, 59999, 0);
112
113     money = 0;
114
115     for (i = 0; i < sol.hc; i++)
116         if (sol.hv[i].t == ITEM_COIN)
117             money += sol.hv[i].n;
118
119     level->score.most_coins.coins[0] = money;
120
121     if (sol.dc > 0)
122         scan_dict(level, &sol);
123
124     /* Compute initial hs default values */
125
126 #define HOP(t, c) \
127     if (t[2] c t[0]) \
128         t[0] = t[1] = t[2]; \
129     else if (t[2] c t[1]) \
130         t[1] = (t[0] + t[2]) / 2
131
132     HOP(level->score.best_times.timer, <=);
133     HOP(level->score.unlock_goal.timer, <=);
134     HOP(level->score.most_coins.coins, >=);
135
136     sol_free(&sol);
137
138     return 1;
139 }
140
141 void level_dump(const struct level *l)
142 {
143     printf("filename:        %s\n"
144            "version:         %s\n"
145            "author:          %s\n"
146            "time limit:      %d\n"
147            "goal count:      %d\n"
148            "time hs:         %d %d %d\n"
149            "goal hs:         %d %d %d\n"
150            "coin hs:         %d %d %d\n"
151            "message:         %s\n"
152            "background:      %s\n"
153            "gradient:        %s\n"
154            "screenshot:      %s\n"
155            "song:            %s\n",
156            l->file,
157            l->version,
158            l->author,
159            l->time,
160            l->goal,
161            l->score.best_times.timer[0],
162            l->score.best_times.timer[1],
163            l->score.best_times.timer[2],
164            l->score.unlock_goal.timer[0],
165            l->score.unlock_goal.timer[1],
166            l->score.unlock_goal.timer[2],
167            l->score.most_coins.coins[0],
168            l->score.most_coins.coins[1],
169            l->score.most_coins.coins[2],
170            l->message,
171            l->back,
172            l->grad,
173            l->shot,
174            l->song);
175 }
176
177 /*---------------------------------------------------------------------------*/
178
179 static unsigned int do_level_init = 1;
180
181 int level_replay(const char *filename)
182 {
183     return demo_replay_init(filename, curr_lg());
184 }
185
186 int level_play(const struct level *l, int m)
187 {
188     struct level_game *lg = curr_lg();
189
190     if (do_level_init)
191     {
192         memset(lg, 0, sizeof (struct level_game));
193
194         lg->mode  = m;
195         lg->level = l;
196         lg->balls = 3;
197     }
198
199     lg->goal = (lg->mode == MODE_PRACTICE) ? 0 : lg->level->goal;
200     lg->time = (lg->mode == MODE_PRACTICE) ? 0 : lg->level->time;
201
202     /* Clear other fields. */
203
204     lg->status = GAME_NONE;
205     lg->coins = 0;
206     lg->timer = lg->time;
207     lg->coin_rank = lg->goal_rank = lg->time_rank =
208         lg->score_rank = lg->times_rank = 3;
209
210     lg->win = lg->dead = lg->unlock = 0;
211     lg->next_level = NULL;
212     lg->bonus = 0;
213
214     return demo_play_init(USER_REPLAY_FILE, lg->level, lg);
215 }
216
217 void level_stat(int status, int clock, int coins)
218 {
219     struct level_game *lg = curr_lg();
220
221     int mode = lg->mode;
222     int timer = (mode == MODE_PRACTICE) ? clock : lg->time - clock;
223
224     char player[MAXNAM];
225
226     config_get_s(CONFIG_PLAYER, player, MAXNAM);
227
228     lg->status = status;
229     lg->coins = coins;
230     lg->timer = timer;
231
232     if (mode == MODE_CHALLENGE)
233     {
234         /* sum time */
235         lg->times += timer;
236
237         /* sum coins an earn extra balls */
238         if (status == GAME_GOAL || lg->level->is_bonus)
239         {
240             lg->balls += count_extra_balls(lg->score, coins);
241             lg->score += coins;
242         }
243
244         /* lose ball and game */
245         else
246         {
247             lg->dead = (lg->balls <= 0);
248             lg->balls--;
249         }
250     }
251
252     set_finish_level(lg, player);
253
254     demo_play_stat(lg);
255 }
256
257 void level_stop(void)
258 {
259     demo_play_stop();
260     do_level_init = 1;
261 }
262
263 int level_next(void)
264 {
265     struct level_game *lg = curr_lg();
266
267     level_stop();
268     lg->level = lg->next_level;
269     do_level_init = 0;
270
271     return level_play(lg->level, lg->mode);
272 }
273
274 int level_same(void)
275 {
276     level_stop();
277     do_level_init = 0;
278     return level_play(curr_lg()->level, curr_lg()->mode);
279 }
280
281 /*---------------------------------------------------------------------------*/
282
283 int count_extra_balls(int old_score, int coins)
284 {
285     return ((old_score % 100) + coins) / 100;
286 }
287
288 void level_update_player_name(void)
289 {
290     char player[MAXNAM];
291
292     config_get_s(CONFIG_PLAYER, player, MAXNAM);
293
294     score_change_name(curr_lg(), player);
295 }
296
297 /*---------------------------------------------------------------------------*/
298
299 const char *status_to_str(int m)
300 {
301     switch (m)
302     {
303     case GAME_NONE:    return _("Aborted");
304     case GAME_TIME:    return _("Time-out");
305     case GAME_GOAL:    return _("Success");
306     case GAME_FALL:    return _("Fall-out");
307     default:           return _("Unknown");
308     }
309 }
310
311 /*---------------------------------------------------------------------------*/