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