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