Replace forgettable view numbers with symbols and clean up view name lookup
[neverball] / ball / level.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 <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <math.h>
19 #include <errno.h>
20 #include <assert.h>
21
22 #include "solid.h"
23 #include "config.h"
24 #include "level.h"
25 #include "set.h"
26
27 /*---------------------------------------------------------------------------*/
28
29 static void scan_level_attribs(struct level *l, const struct s_file *fp)
30 {
31     int i;
32
33     int have_goal = 0, have_time = 0;
34     int need_bt_easy = 0, need_fu_easy = 0, need_mc_easy = 0;
35
36     for (i = 0; i < fp->dc; i++)
37     {
38         char *k = fp->av + fp->dv[i].ai;
39         char *v = fp->av + fp->dv[i].aj;
40
41         if (strcmp(k, "message") == 0)
42             strncpy(l->message, v, MAXSTR);
43         else if (strcmp(k, "song") == 0)
44             strncpy(l->song, v, PATHMAX);
45         else if (strcmp(k, "shot") == 0)
46             strncpy(l->shot, v, PATHMAX);
47         else if (strcmp(k, "goal") == 0)
48         {
49             l->goal = atoi(v);
50             have_goal = 1;
51         }
52         else if (strcmp(k, "time") == 0)
53         {
54             l->time = atoi(v);
55             have_time = 1;
56         }
57         else if (strcmp(k, "time_hs") == 0)
58         {
59             switch (sscanf(v, "%d %d %d",
60                            &l->score.best_times.timer[0],
61                            &l->score.best_times.timer[1],
62                            &l->score.best_times.timer[2]))
63             {
64             case 2: need_bt_easy = 1; break;
65             case 3:                   break;
66
67             default:
68                 /* TODO, complain loudly? */
69                 break;
70             }
71         }
72         else if (strcmp(k, "goal_hs") == 0)
73         {
74             switch (sscanf(v, "%d %d %d",
75                            &l->score.fast_unlock.timer[0],
76                            &l->score.fast_unlock.timer[1],
77                            &l->score.fast_unlock.timer[2]))
78             {
79             case 2: need_fu_easy = 1; break;
80             case 3:                   break;
81
82             default:
83                 /* TODO, complain loudly? */
84                 break;
85             }
86         }
87         else if (strcmp(k, "coin_hs") == 0)
88         {
89             switch (sscanf(v, "%d %d %d",
90                            &l->score.most_coins.coins[0],
91                            &l->score.most_coins.coins[1],
92                            &l->score.most_coins.coins[2]))
93             {
94             case 2: need_mc_easy = 1; break;
95             case 3:                   break;
96
97             default:
98                 /* TODO, complain loudly? */
99                 break;
100             }
101         }
102         else if (strcmp(k, "version") == 0)
103             strncpy(l->version, v, MAXSTR);
104         else if (strcmp(k, "author") == 0)
105             strncpy(l->author, v, MAXSTR);
106         else if (strcmp(k, "bonus") == 0)
107             l->is_bonus = atoi(v) ? 1 : 0;
108     }
109
110     if (have_goal)
111     {
112         if (need_mc_easy)
113             l->score.most_coins.coins[2] = l->goal;
114
115         l->score.fast_unlock.coins[0] =
116             l->score.fast_unlock.coins[1] =
117             l->score.fast_unlock.coins[2] = l->goal;
118     }
119
120     if (have_time)
121     {
122         if (need_bt_easy)
123             l->score.best_times.timer[2] = l->time;
124         if (need_fu_easy)
125             l->score.fast_unlock.timer[2] = l->time;
126     }
127 }
128
129 int level_load(const char *filename, struct level *level)
130 {
131     struct s_file sol;
132
133     int money;
134     int i;
135
136     memset(level, 0, sizeof (struct level));
137     memset(&sol,  0, sizeof (sol));
138
139 #define format \
140     L_("Error while loading level file '%s': %s\n")
141 #define default_error \
142     L_("Not a valid level file")
143
144     if (!sol_load_only_head(&sol, filename))
145     {
146         const char *error = errno ? strerror(errno) : default_error;
147         fprintf(stderr, format, filename, error);
148         return 0;
149     }
150
151 #undef format
152 #undef default_error
153
154     strncpy(level->file, filename, PATHMAX - 1);
155
156     score_init_hs(&level->score.best_times, 59999, 0);
157     score_init_hs(&level->score.fast_unlock, 59999, 0);
158     score_init_hs(&level->score.most_coins, 59999, 0);
159
160     money = 0;
161
162     for (i = 0; i < sol.hc; i++)
163         if (sol.hv[i].t == ITEM_COIN)
164             money += sol.hv[i].n;
165
166     level->score.most_coins.coins[0] = money;
167
168     scan_level_attribs(level, &sol);
169
170     /* Compute initial hs default values */
171
172 #define HOP(t, c) \
173     if (t[2] c t[0]) \
174         t[0] = t[1] = t[2]; \
175     else if (t[2] c t[1]) \
176         t[1] = (t[0] + t[2]) / 2
177
178     HOP(level->score.best_times.timer, <=);
179     HOP(level->score.fast_unlock.timer, <=);
180     HOP(level->score.most_coins.coins, >=);
181
182     sol_free(&sol);
183
184     return 1;
185 }
186
187 /*---------------------------------------------------------------------------*/
188
189 int  level_exists(int i)
190 {
191     return set_level_exists(curr_set(), i);
192 }
193
194 void level_open(int i)
195 {
196     if (level_exists(i))
197         get_level(i)->is_locked = 0;
198 }
199
200 int  level_opened(int i)
201 {
202     return level_exists(i) && !get_level(i)->is_locked;
203 }
204
205 void level_complete(int i)
206 {
207     if (level_exists(i))
208         get_level(i)->is_completed = 1;
209 }
210
211 int  level_completed(int i)
212 {
213     return level_exists(i) && get_level(i)->is_completed;
214 }
215
216 int  level_time (int i)
217 {
218     assert(level_exists(i));
219     return get_level(i)->time;
220 }
221
222 int  level_goal (int i)
223 {
224     assert(level_exists(i));
225     return get_level(i)->goal;
226 }
227
228 int  level_bonus(int i)
229 {
230     return level_exists(i) && get_level(i)->is_bonus;
231 }
232
233 const char *level_shot(int i)
234 {
235     return level_exists(i) ? get_level(i)->shot : NULL;
236 }
237
238 const char *level_file(int i)
239 {
240     return level_exists(i) ? get_level(i)->file : NULL;
241 }
242
243 const char *level_name(int i)
244 {
245     return level_exists(i) ? get_level(i)->name : NULL;
246 }
247
248 const char *level_msg(int i)
249 {
250     if (level_exists(i) && strlen(get_level(i)->message) > 0)
251         return _(get_level(i)->message);
252
253     return NULL;
254 }
255
256 /*---------------------------------------------------------------------------*/
257
258 int level_score_update(int level,
259                        int timer,
260                        int coins,
261                        int *time_rank,
262                        int *goal_rank,
263                        int *coin_rank)
264 {
265     struct level *l = get_level(level);
266     char player[MAXSTR] = "";
267
268     config_get_s(CONFIG_PLAYER, player, MAXSTR);
269
270     if (time_rank)
271         *time_rank = score_time_insert(&l->score.best_times,
272                                        player, timer, coins);
273
274     if (goal_rank)
275         *goal_rank = score_time_insert(&l->score.fast_unlock,
276                                        player, timer, coins);
277
278     if (coin_rank)
279         *coin_rank = score_coin_insert(&l->score.most_coins,
280                                        player, timer, coins);
281
282     if ((time_rank && *time_rank < 3) ||
283         (goal_rank && *goal_rank < 3) ||
284         (coin_rank && *coin_rank < 3))
285         return 1;
286     else
287         return 0;
288 }
289
290 void level_rename_player(int level,
291                          int time_rank,
292                          int goal_rank,
293                          int coin_rank,
294                          const char *player)
295 {
296     struct level *l = get_level(level);
297
298     strncpy(l->score.best_times.player [time_rank], player, MAXNAM);
299     strncpy(l->score.fast_unlock.player[goal_rank], player, MAXNAM);
300     strncpy(l->score.most_coins.player [coin_rank], player, MAXNAM);
301 }
302
303 /*---------------------------------------------------------------------------*/
304