Make config_get_s return a pointer, not copy to a passed-in buffer
[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) == 0 ?
152              curr_clock() :
153              level_time(level) - curr_clock());
154
155     switch (status)
156     {
157     case GAME_GOAL:
158
159         for (i = curr.score + 1; i <= curr.score + coins; i++)
160             if (progress_reward_ball(i))
161                 curr.balls++;
162
163         curr.score += coins;
164         curr.times += timer;
165
166         dirty = level_score_update(level, timer, coins,
167                                    &time_rank,
168                                    goal == 0 ? &goal_rank : NULL,
169                                    &coin_rank);
170
171         if (!level_completed(level))
172         {
173             level_complete(level);
174             dirty = 1;
175         }
176
177         /* Compute next level. */
178
179         if (mode == MODE_CHALLENGE)
180         {
181             for (next = level + 1; level_bonus(next); next++)
182                 if (!level_opened(next))
183                 {
184                     level_open(next);
185                     dirty = 1;
186                     bonus++;
187                 }
188         }
189         else
190         {
191             for (next = level + 1;
192                  level_bonus(next) && !level_opened(next);
193                  next++)
194                 /* Do nothing. */;
195         }
196
197         /* Open next level or complete the set. */
198
199         if (level_exists(next))
200         {
201             level_open(next);
202             dirty = 1;
203         }
204         else
205             done = mode == MODE_CHALLENGE;
206
207         break;
208
209     case GAME_FALL:
210         /* Fall through. */
211
212     case GAME_TIME:
213         for (next = level + 1;
214              level_exists(next) && !level_opened(next);
215              next++)
216             /* Do nothing. */;
217
218         curr.times += timer;
219         curr.balls -= 1;
220
221         break;
222     }
223
224     if (dirty)
225         set_store_hs();
226
227     demo_play_stat(status, coins, timer);
228 }
229
230 void progress_stop(void)
231 {
232     demo_play_stop();
233 }
234
235 void progress_exit(void)
236 {
237     assert(done);
238
239     if (set_score_update(curr.times, curr.score, &score_rank, &times_rank))
240         set_store_hs();
241 }
242
243 int  progress_replay(const char *filename)
244 {
245     if (demo_replay_init(filename, &goal, &mode,
246                          &curr.balls,
247                          &curr.score,
248                          &curr.times))
249     {
250         goal_i = goal;
251         replay = 1;
252         return 1;
253     }
254     else
255         return 0;
256 }
257
258 int  progress_next_avail(void)
259 {
260     if (mode == MODE_CHALLENGE)
261         return status == GAME_GOAL && level_exists(next);
262     else
263         return level_opened(next);
264 }
265
266 int  progress_same_avail(void)
267 {
268     switch (status)
269     {
270     case GAME_NONE:
271         return mode != MODE_CHALLENGE;
272
273     default:
274         if (mode == MODE_CHALLENGE)
275             return !progress_dead();
276         else
277             return 1;
278     }
279 }
280
281 int  progress_next(void)
282 {
283     progress_stop();
284     return progress_play(next);
285 }
286
287 int  progress_same(void)
288 {
289     progress_stop();
290
291     /* Reset progress and goal enabled state. */
292
293     if (status == GAME_GOAL)
294         curr = prev;
295
296     same_goal_e = 1;
297
298     return progress_play(level);
299 }
300
301 int  progress_dead(void)
302 {
303     return mode == MODE_CHALLENGE ? curr.balls < 0 : 0;
304 }
305
306 int  progress_done(void)
307 {
308     return done;
309 }
310
311 int  progress_last(void)
312 {
313     return mode != MODE_CHALLENGE && status == GAME_GOAL && !level_exists(next);
314 }
315
316 int  progress_lvl_high(void)
317 {
318     return time_rank < 3 || goal_rank < 3 || coin_rank < 3;
319 }
320
321 int  progress_set_high(void)
322 {
323     return score_rank < 3 || times_rank < 3;
324 }
325
326 void progress_rename(int set_only)
327 {
328     const char *player = config_get_s(CONFIG_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 /*---------------------------------------------------------------------------*/