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