glasstower: smooth glass goal
[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.balls--;
210         break;
211     }
212
213     if (dirty)
214         set_store_hs();
215
216     demo_play_stat(status, coins, timer);
217 }
218
219 void progress_stop(void)
220 {
221     demo_play_stop();
222 }
223
224 void progress_exit(void)
225 {
226     assert(done);
227
228     if (set_score_update(curr.times, curr.score, &score_rank, &times_rank))
229         set_store_hs();
230 }
231
232 int  progress_replay(const char *filename)
233 {
234     if (demo_replay_init(filename, &goal, &mode,
235                          &curr.balls,
236                          &curr.score,
237                          &curr.times))
238     {
239         goal_i = goal;
240         return 1;
241     }
242     else
243         return 0;
244 }
245
246 int  progress_next_avail(void)
247 {
248     if (mode == MODE_CHALLENGE)
249         return status == GAME_GOAL && level_exists(next);
250     else
251         return level_opened(next);
252 }
253
254 int  progress_same_avail(void)
255 {
256     switch (status)
257     {
258     case GAME_NONE:
259         return mode != MODE_CHALLENGE;
260
261     default:
262         if (mode == MODE_CHALLENGE)
263             return !progress_dead();
264         else
265             return 1;
266     }
267 }
268
269 int  progress_next(void)
270 {
271     progress_stop();
272     return progress_play(next);
273 }
274
275 int  progress_same(void)
276 {
277     progress_stop();
278
279     /* Reset progress and goal enabled state. */
280
281     if (status == GAME_GOAL)
282     {
283         curr = prev;
284         same_goal_e = 1;
285     }
286
287     return progress_play(level);
288 }
289
290 int  progress_dead(void)
291 {
292     return mode == MODE_CHALLENGE ? curr.balls < 0 : 0;
293 }
294
295 int  progress_done(void)
296 {
297     return done;
298 }
299
300 int  progress_lvl_high(void)
301 {
302     return time_rank < 3 || goal_rank < 3 || coin_rank < 3;
303 }
304
305 int  progress_set_high(void)
306 {
307     return score_rank < 3 || times_rank < 3;
308 }
309
310 void progress_rename(int set_only)
311 {
312     char player[MAXNAM] = "";
313
314     config_get_s(CONFIG_PLAYER, player, sizeof (player));
315
316     if (set_only)
317     {
318         set_rename_player(score_rank, times_rank, player);
319     }
320     else
321     {
322         level_rename_player(level, time_rank, goal_rank, coin_rank, player);
323
324         demo_rename_player(USER_REPLAY_FILE, player);
325
326         if (progress_done())
327             set_rename_player(score_rank, times_rank, player);
328     }
329
330     set_store_hs();
331 }
332
333 int  progress_reward_ball(int s)
334 {
335     return s > 0 && s % 100 == 0;
336 }
337
338 /*---------------------------------------------------------------------------*/
339
340 int curr_level(void) { return level;      }
341 int curr_balls(void) { return curr.balls; }
342 int curr_score(void) { return curr.score; }
343 int curr_mode (void) { return mode;       }
344 int curr_bonus(void) { return bonus;      }
345 int curr_goal (void) { return goal;       }
346
347 int progress_time_rank(void) { return time_rank; }
348 int progress_goal_rank(void) { return goal_rank; }
349 int progress_coin_rank(void) { return coin_rank; }
350
351 int progress_times_rank(void) { return times_rank; }
352 int progress_score_rank(void) { return score_rank; }
353
354 /*---------------------------------------------------------------------------*/
355
356 const char *mode_to_str(int m, int l)
357 {
358     switch (m)
359     {
360     case MODE_CHALLENGE: return l ? _("Challenge Mode") : _("Challenge");
361     case MODE_NORMAL:    return l ? _("Normal Mode")    : _("Normal");
362     default:             return l ? _("Unknown Mode")   : _("Unknown");
363     }
364 }
365
366 /*---------------------------------------------------------------------------*/