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