1 /* level.c - challenge the user in a controlled way
2 *
3 * Copyright 1999 Jochen Voss. */
4
5 static const char rcsid[] = "$Id: level.c 4839 2003-04-13 16:50:02Z voss $";
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include <assert.h>
12
13 #include "moon-buggy.h"
14
15
16 /* The length of the inter-level gap (measured in ticks) */
17 #define PAUSE 40
18
19
20 static int hole, plateau;
21 static int is_edge, crater_seen;
22 static int level, initial_level, last_level, ticks;
23
24 static union {
25 struct {
26 int state, gap;
27 } l0;
28 struct {
29 int state, gap;
30 } l1;
31 struct {
32 int state, gap;
33 } l2;
34 struct {
35 int state, gap, pos;
36 } l3;
37 struct {
38 int state, gap, next_gap;
39 } l4;
40 /* l5 */
41 struct {
42 int state, gap, next_gap, spare_time;
43 } l6;
44 struct {
45 int state, gap, next_gap, spare_time;
46 } l_fin;
47 } data;
48
49
50 static void
level0_init(void)51 level0_init (void)
52 {
53 hole = 2;
54 data.l0.state = 0;
55 }
56
57 static void
level0(double t)58 level0 (double t)
59 {
60 if (is_edge) {
61 if (ticks < 345) {
62 data.l0.gap = 14.5 - ticks*4.0/345 + uniform_rnd (4.5 + ticks*4.0/345);
63 } else {
64 switch (data.l0.state) {
65 case 0:
66 data.l0.gap = 13;
67 break;
68 case 1:
69 data.l0.gap = 10;
70 break;
71 case 2:
72 data.l0.gap = 11;
73 break;
74 default:
75 ++level;
76 break;
77 }
78 ++data.l0.state;
79 }
80 }
81
82 --data.l0.gap;
83 if (data.l0.gap <= 0) hole = 2;
84 }
85
86
87 static void
level1_init(void)88 level1_init (void)
89 {
90 hole = 5;
91 data.l1.state = 0;
92 }
93
94 static void
level1(double t)95 level1 (double t)
96 {
97 static const int table [11] = { 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4 };
98 if (is_edge) {
99 if (ticks < 300) {
100 data.l1.gap = 14.5 - ticks*4.0/300 + uniform_rnd (3);
101 } else {
102 switch (data.l1.state) {
103 default:
104 data.l1.gap = 12;
105 break;
106 case 1:
107 data.l1.gap = 7;
108 break;
109 case 2:
110 data.l1.gap = 8;
111 break;
112 }
113 if (data.l1.state > 2) {
114 ++level;
115 } else {
116 ++data.l1.state;
117 }
118 }
119 }
120
121 --data.l1.gap;
122 if (data.l1.gap <= 0) {
123 switch (data.l1.state) {
124 case 1:
125 case 2:
126 hole = 4;
127 break;
128 case 3:
129 hole = 5;
130 break;
131 default:
132 hole = table [uniform_rnd (11)];
133 break;
134 }
135 }
136 }
137
138
139 static void
level2_init(void)140 level2_init (void)
141 {
142 hole = 2;
143 data.l2.state = 0;
144 }
145
146 static void
level2(double t)147 level2 (double t)
148 {
149 if (is_edge) {
150 if (data.l2.state == 0) {
151 data.l2.gap = 8;
152 if (ticks >= 310) {
153 data.l2.state = -1;
154 } else {
155 data.l2.state = 2 + uniform_rnd (3 + (ticks < 190));
156 }
157 } else if (data.l2.state > 0) {
158 data.l2.gap = 12 + uniform_rnd (6);
159 --data.l2.state;
160 } else {
161 switch (data.l2.state) {
162 case -1:
163 data.l2.gap = 16;
164 break;
165 case -2:
166 data.l2.gap = 13;
167 break;
168 case -4:
169 data.l2.gap = 14;
170 break;
171 default:
172 data.l2.gap = 7;
173 break;
174 }
175 --data.l2.state;
176 }
177 }
178 --data.l2.gap;
179 if (data.l2.gap <= 0) hole = 2;
180 if (data.l2.state < -6) ++level;
181 }
182
183
184 static void
level3_init(void)185 level3_init (void)
186 {
187 data.l3.state = -1;
188 }
189
190 static void
level3(double t)191 level3 (double t)
192 {
193 if (data.l3.state < 0) {
194 place_meteor ();
195 data.l3.state = 0;
196 }
197
198 if (is_edge) {
199 data.l3.gap = 20 + uniform_rnd (10);
200 if (ticks > 160) ++data.l3.state;
201 switch (data.l3.state) {
202 case 0:
203 data.l3.pos = uniform_rnd (5)+1;
204 break;
205 case 1:
206 data.l3.pos = data.l3.gap - uniform_rnd (7) - 1;
207 break;
208 case 2:
209 data.l3.pos = data.l3.gap - uniform_rnd (2) - 1;
210 break;
211 default:
212 bonus[0] += 20;
213 ++level;
214 break;
215 }
216 }
217
218 --data.l3.gap;
219 if (data.l3.gap == data.l3.pos) place_meteor ();
220 if (data.l3.gap <= 0) hole = 2;
221 }
222
223
224 static void
level4_init(void)225 level4_init (void)
226 {
227 hole = 6;
228 data.l4.next_gap = 8 + uniform_rnd (10);
229 data.l4.state = 3;
230 }
231
232 static void
level4(double t)233 level4 (double t)
234 {
235 if (is_edge) {
236 data.l4.gap = data.l4.next_gap;
237 if (data.l4.state == 0 && ticks < 700) {
238 data.l4.state = 3 + uniform_rnd (3 + 2*(ticks < 350));
239 } else {
240 --data.l4.state;
241 }
242 if (data.l4.state < -5) ++level;
243 }
244 --data.l4.gap;
245 if (data.l4.gap <= 0) {
246 switch (data.l4.state) {
247 default:
248 hole = 2 + uniform_rnd (3);
249 data.l4.next_gap = 14 + uniform_rnd (6) - hole;
250 break;
251 case 0:
252 if (uniform_rnd (2)) {
253 hole = 5;
254 data.l4.next_gap = 9 + uniform_rnd (10);
255 } else {
256 hole = 2;
257 data.l4.next_gap = 8;
258 }
259 break;
260 case -1:
261 hole = 3;
262 data.l4.next_gap = 14;
263 break;
264 case -2:
265 hole = 6;
266 data.l4.next_gap = 6;
267 break;
268 case -3:
269 hole = 4;
270 data.l4.next_gap = 16 + uniform_rnd (6);
271 break;
272 case -4:
273 hole = 2;
274 data.l4.next_gap = 7;
275 break;
276 case -5:
277 hole = 5;
278 data.l4.next_gap = 12;
279 break;
280 }
281 }
282 }
283
284
285 static void
level5(double t)286 level5 (double t)
287 {
288 if (uniform_rnd (ticks % 20 < 8 ? 4 : 8) == 0) place_meteor ();
289 if (ticks >= 125) ++level;
290 }
291
292
293 static void
level6_init(void)294 level6_init (void)
295 {
296 hole = 5;
297 data.l6.next_gap = 8 + uniform_rnd (10);
298 data.l6.state = 3;
299 data.l6.spare_time = 6;
300 }
301
302 static void
level6(double t)303 level6 (double t)
304 {
305 int slip; /* this many ticks/meteor may the user vaste */
306
307 if (is_edge) {
308 data.l6.gap = data.l6.next_gap;
309 if (data.l6.state == 0) {
310 data.l6.state = 3 + uniform_rnd (5);
311 if (ticks >= 375) ++level;
312 } else {
313 --data.l6.state;
314 }
315 }
316 --data.l6.gap;
317 ++data.l6.spare_time;
318 if (data.l6.gap <= 0) {
319 if (data.l6.state) {
320 hole = 2 + uniform_rnd (3);
321 data.l6.next_gap = 15 + uniform_rnd (6) - hole;
322 } else {
323 if (uniform_rnd (3)) {
324 hole = 5;
325 data.l6.next_gap = 9 + uniform_rnd (10);
326 } else {
327 hole = 2 + uniform_rnd (2);
328 data.l6.next_gap = 9;
329 }
330 }
331 data.l6.spare_time -= 11;
332 }
333
334 slip = (380-ticks)*3.0/374 + 1.5;
335 if (data.l6.spare_time >= 3+slip) {
336 if (uniform_rnd (data.l6.spare_time) > 2) {
337 place_meteor ();
338 data.l6.spare_time -= 3+slip;
339 }
340 }
341 }
342
343
344 static void
level_fin_init(void)345 level_fin_init (void)
346 {
347 data.l_fin.state = uniform_rnd (10);
348 hole = 2;
349 data.l_fin.next_gap = 7;
350 data.l_fin.spare_time = 0;
351 }
352
353 static void
level_fin(double t)354 level_fin (double t)
355 {
356 if (is_edge) {
357 data.l_fin.gap = data.l_fin.next_gap;
358 if (data.l_fin.state == 0) {
359 data.l_fin.state = 3 + uniform_rnd (6);
360 } else {
361 --data.l_fin.state;
362 }
363 }
364 --data.l_fin.gap;
365 ++data.l_fin.spare_time;
366 if (data.l_fin.gap <= 0) {
367 if (data.l_fin.state) {
368 hole = 2 + uniform_rnd (3);
369 data.l_fin.next_gap = 15 + uniform_rnd (6) - hole;
370 } else {
371 if (uniform_rnd (3)) {
372 hole = 5 + uniform_rnd (2);
373 data.l_fin.next_gap = 9 + uniform_rnd (10);
374 } else {
375 hole = 2 + uniform_rnd (3);
376 data.l_fin.next_gap = 9;
377 }
378 }
379 data.l_fin.spare_time -= 11;
380 }
381
382 if (data.l_fin.spare_time >= 5) {
383 if (uniform_rnd (data.l_fin.spare_time) > 2) {
384 place_meteor ();
385 data.l_fin.spare_time -= 5;
386 }
387 }
388 }
389
390 #define LEVEL_COUNT 8
391
392 static struct {
393 void (*init_fn) (void);
394 void (*fn) (double t);
395 const char *msg;
396 } levels [LEVEL_COUNT] = {
397 { level0_init, level0, "good luck (or use <SPC> to jump)" },
398 { level1_init, level1, "some craters are wider than others" },
399 { level2_init, level2 },
400 { level3_init, level3, "use <a> to fire the laser" },
401 { level4_init, level4 },
402 { NULL, level5, "... but what is that?" },
403 { level6_init, level6 },
404 { level_fin_init, level_fin, "You may start your journey, now." }
405 };
406
407 void
level_start(int initial)408 level_start (int initial)
409 /* Start the game anew with level INITIAL. */
410 {
411 assert (initial >= 0 && initial < LEVEL_COUNT);
412 level = initial_level = initial;
413 last_level = -1;
414 ticks = 0;
415 }
416
417 static void
score_crater(int width)418 score_crater (int width)
419 {
420 static const int score_table [] = { 0, 0, 4, 8, 9, 16, 32 };
421 assert (width < 7);
422 bonus[0] += score_table [width];
423 }
424
425 static void
score_plateau(int width)426 score_plateau (int width)
427 {
428 static const int score_table [] = { 0, 0, 0, 0, 0, 0, 32, 18, 9 };
429 if (width > 8) return;
430 bonus[0] += score_table [width];
431 }
432
433 void
level_tick(double t)434 level_tick (double t)
435 /* Advance the current level's state by one.
436 * The function must be called every time the ground moved. It fills
437 * in the new values of `ground2[0]' and `bonus[0]'. The parameter T
438 * must be the current game time. */
439 {
440 int ground;
441
442 bonus[0] = 0;
443 if (level != last_level) {
444 double msg_t;
445 level = level % LEVEL_COUNT;
446 hole = 0;
447 if (levels[level].init_fn) levels[level].init_fn ();
448 if (last_level >= 0) {
449 ticks = -PAUSE;
450 msg_t = t + TICK(car_x + PAUSE/5);
451 } else {
452 msg_t = t;
453 }
454 if (levels[level].msg) {
455 add_event (msg_t, print_hint_h, (void *)levels[level].msg);
456 }
457 last_level = level;
458 is_edge = 1;
459 crater_seen = 0;
460 }
461 if (ticks < 0) {
462 ground = '#';
463 } else if (hole > 0) {
464 if (! crater_seen) {
465 score_crater (hole);
466 is_edge = 1;
467 crater_seen = 1;
468 plateau = 0;
469 }
470
471 ground = ' ';
472 --hole;
473 } else {
474 levels[level].fn (t);
475 is_edge = 0;
476 crater_seen = 0;
477
478 ground = '#';
479 ++plateau;
480 if (hole > 0) score_plateau (plateau);
481 }
482
483 ground2[0] = ground;
484 print_buggy(); /* ++pg now the refresh of the car is needed here. */
485
486 ++ticks;
487 }
488
489 int
current_level(void)490 current_level (void)
491 /* Return the current level's number as the car sees it. */
492 {
493 int res = (ticks+PAUSE/2 >= car_x) ? level : level - 1;
494 if (res < initial_level) res = initial_level;
495 return res;
496 }
497