Add time from unsuccessful runs to total set time
[neverball] / ball / progress.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 "progress.h"
16 #include "config.h"
17 #include "game.h"
18 #include "demo.h"
19 #include "level.h"
20 #include "set.h"
21 #include "lang.h"
22 #include "score.h"
23
24 #include <assert.h>
25
26 /*---------------------------------------------------------------------------*/
27
28 struct progress
29 {
30     int balls;
31     int score;
32     int times;
33 };
34
35 static int mode = MODE_NORMAL;
36
37 static int level =  0;
38 static int next  = -1;
39 static int done  =  0;
40
41 static int bonus =  0;
42
43 static struct progress curr;
44 static struct progress prev;
45
46 /* Set stats. */
47
48 static int score_rank = 3;
49 static int times_rank = 3;
50
51 /* Level stats. */
52
53 static int status = GAME_NONE;
54
55 static int coins = 0;
56 static int timer = 0;
57
58 static int goal   = 0; /* Current goal value. */
59 static int goal_i = 0; /* Initial goal value. */
60
61 static int goal_e      = 0; /* Goal enabled flag                */
62 static int same_goal_e = 0; /* Reuse existing goal enabled flag */
63
64 static int time_rank = 3;
65 static int goal_rank = 3;
66 static int coin_rank = 3;
67
68 /*---------------------------------------------------------------------------*/
69
70 void progress_init(int m)
71 {
72     mode  = m;
73     bonus = 0;
74
75     curr.balls = 2;
76     curr.score = 0;
77     curr.times = 0;
78
79     prev = curr;
80
81     score_rank = times_rank = 3;
82
83     done  = 0;
84 }
85
86 int  progress_play(int i)
87 {
88     if (level_opened(i) || config_cheat())
89     {
90         level = i;
91
92         next   = -1;
93         status = GAME_NONE;
94         coins  = 0;
95         timer  = 0;
96         goal   = goal_i = level_goal(level);
97
98         if (same_goal_e)
99             same_goal_e = 0;
100         else
101             goal_e = (mode != MODE_CHALLENGE && level_completed(level) &&
102                       config_get_d(CONFIG_LOCK_GOALS) == 0) || goal == 0;
103
104         prev = curr;
105
106         time_rank = goal_rank = coin_rank = 3;
107
108         if (demo_play_init(USER_REPLAY_FILE, get_level(level), mode,
109                            level_time(level), level_goal(level),
110                            goal_e, curr.score, curr.balls, curr.times))
111         {
112             return 1;
113         }
114         else
115         {
116             demo_play_stop();
117             return 0;
118         }
119     }
120     return 0;
121 }
122
123 void progress_step(void)
124 {
125     if (goal > 0)
126     {
127         goal = goal_i - curr_coins();
128
129         if (goal <= 0)
130         {
131             game_set_goal();
132             goal = 0;
133         }
134     }
135 }
136
137 void progress_stat(int s)
138 {
139     int i, dirty = 0;
140
141     status = s;
142
143     coins = curr_coins();
144     timer = level_time(level) - curr_clock();
145
146     switch (status)
147     {
148     case GAME_GOAL:
149
150         for (i = curr.score + 1; i <= curr.score + coins; i++)
151             if (progress_reward_ball(i))
152                 curr.balls++;
153
154         curr.score += coins;
155         curr.times += timer;
156
157         dirty = level_score_update(level, timer, coins,
158                                    &time_rank,
159                                    goal == 0 ? &goal_rank : NULL,
160                                    &coin_rank);
161
162         if (!level_completed(level))
163         {
164             level_complete(level);
165             dirty = 1;
166         }
167
168         /* Compute next level. */
169
170         if (mode == MODE_CHALLENGE)
171         {
172             for (next = level + 1; level_bonus(next); next++)
173                 if (!level_opened(next))
174                 {
175                     level_open(next);
176                     dirty = 1;
177                     bonus++;
178                 }
179         }
180         else
181         {
182             for (next = level + 1;
183                  level_bonus(next) && !level_opened(next);
184                  next++)
185                 /* Do nothing. */;
186         }
187
188         /* Open next level or complete the set. */
189
190         if (level_exists(next))
191         {
192             level_open(next);
193             dirty = 1;
194         }
195         else
196             done = mode == MODE_CHALLENGE;
197
198         break;
199
200     case GAME_FALL:
201         /* Fall through. */
202
203     case GAME_TIME:
204         for (next = level + 1;
205              level_exists(next) && !level_opened(next);
206              next++)
207             /* Do nothing. */;
208
209         curr.times += timer;
210         curr.balls -= 1;
211
212         break;
213     }
214
215     if (dirty)
216         set_store_hs();
217
218     demo_play_stat(status, coins, timer);
219 }
220
221 void progress_stop(void)
222 {
223     demo_play_stop();
224 }
225
226 void progress_exit(void)
227 {
228     assert(done);
229
230     if (set_score_update(curr.times, curr.score, &score_rank, &times_rank))
231         set_store_hs();
232 }
233
234 int  progress_replay(const char *filename)
235 {
236     if (demo_replay_init(filename, &goal, &mode,
237                          &curr.balls,
238                          &curr.score,
239                          &curr.times))
240     {
241         goal_i = goal;
242         return 1;
243     }
244     else
245         return 0;
246 }
247
248 int  progress_next_avail(void)
249 {
250     if (mode == MODE_CHALLENGE)
251         return status == GAME_GOAL && level_exists(next);
252     else
253         return level_opened(next);
254 }
255
256 int  progress_same_avail(void)
257 {
258     switch (status)
259     {
260     case GAME_NONE:
261         return mode != MODE_CHALLENGE;
262
263     default:
264         if (mode == MODE_CHALLENGE)
265             return !progress_dead();
266         else
267             return 1;
268     }
269 }
270
271 int  progress_next(void)
272 {
273     progress_stop();
274     return progress_play(next);
275 }
276
277 int  progress_same(void)
278 {
279     progress_stop();
280
281     /* Reset progress and goal enabled state. */
282
283     if (status == GAME_GOAL)
284     {
285         curr = prev;
286         same_goal_e = 1;
287     }
288
289     return progress_play(level);
290 }
291
292 int  progress_dead(void)
293 {
294     return mode == MODE_CHALLENGE ? curr.balls < 0 : 0;
295 }
296
297 int  progress_done(void)
298 {
299     return done;
300 }
301
302 int  progress_lvl_high(void)
303 {
304     return time_rank < 3 || goal_rank < 3 || coin_rank < 3;
305 }
306
307 int  progress_set_high(void)
308 {
309     return score_rank < 3 || times_rank < 3;
310 }
311
312 void progress_rename(int set_only)
313 {
314     char player[MAXNAM] = "";
315
316     config_get_s(CONFIG_PLAYER, player, sizeof (player));
317
318     if (set_only)
319     {
320         set_rename_player(score_rank, times_rank, player);
321     }
322     else
323     {
324         level_rename_player(level, time_rank, goal_rank, coin_rank, player);
325
326         demo_rename_player(USER_REPLAY_FILE, player);
327
328         if (progress_done())
329             set_rename_player(score_rank, times_rank, player);
330     }
331
332     set_store_hs();
333 }
334
335 int  progress_reward_ball(int s)
336 {
337     return s > 0 && s % 100 == 0;
338 }
339
340 /*---------------------------------------------------------------------------*/
341
342 int curr_level(void) { return level;      }
343 int curr_balls(void) { return curr.balls; }
344 int curr_score(void) { return curr.score; }
345 int curr_mode (void) { return mode;       }
346 int curr_bonus(void) { return bonus;      }
347 int curr_goal (void) { return goal;       }
348
349 int progress_time_rank(void) { return time_rank; }
350 int progress_goal_rank(void) { return goal_rank; }
351 int progress_coin_rank(void) { return coin_rank; }
352
353 int progress_times_rank(void) { return times_rank; }
354 int progress_score_rank(void) { return score_rank; }
355
356 /*---------------------------------------------------------------------------*/
357
358 const char *mode_to_str(int m, int l)
359 {
360     switch (m)
361     {
362     case MODE_CHALLENGE: return l ? _("Challenge Mode") : _("Challenge");
363     case MODE_NORMAL:    return l ? _("Normal Mode")    : _("Normal");
364     default:             return l ? _("Unknown Mode")   : _("Unknown");
365     }
366 }
367
368 /*---------------------------------------------------------------------------*/