1 #include "ladder.h"
2
3 static struct
4 {
5 int row, col; /* current coordinates */
6 int st_row, st_col; /* start coordinates */
7 DIR dir;
8 int jst;
9 } lad;
10
11 static char laddirs[] = "gbdqp";
12
13 typedef struct
14 {
15 int row, col;
16 DIR dir;
17 int launch;
18 } DER;
19 static DER *ders = 0;
20
21 typedef struct { int row, col; } RELEASE;
22 static RELEASE releases[3];
23 static int bonus;
24 static char bg[DIMROW][DIMCOL];
25
26 /* these extra "00" in score & bonus are extremly silly -
27 but that is how the original did it */
stat_lads()28 static void stat_lads() { mvprintw(DIMROW,0,"Lads %3d",lads); }
stat_level()29 static void stat_level() { mvprintw(DIMROW,14,"Level %3d",level + 1); }
stat_score()30 static void stat_score() { mvprintw(DIMROW,29,"Score %4d00",score); }
stat_bonus()31 static void stat_bonus() { mvprintw(DIMROW,59,"Bonus time %4d00",bonus); }
32
add_score(int add)33 static void add_score(int add)
34 {
35 if( score / 100 < (score + add) / 100 )
36 lads++, stat_lads();
37 score += add;
38 stat_score();
39 }
40
ldscreen(void)41 static void ldscreen(void)
42 {
43 int row,i;
44 RELEASE *rel = releases;
45 char *s,*t;
46
47 memset(bg,' ',sizeof(bg));
48 for( row = 0; row < DIMROW; row++ )
49 {
50 s = screens[scrno][row];
51 t = bg[row];
52 memcpy(t,unmerge(s),strlen(unmerge(s)));
53 t[DIMCOL-1] = '\0';
54 mvaddstr(row,0,t);
55
56 /* find points of release */
57 for( s = t; (s = strchr(s,CRELEAS)); s++ )
58 {
59 rel->row = row;
60 rel->col = s - t;
61 rel++;
62 }
63 }
64 /* mark the rest of releases */
65 for( ; rel < &releases[DIM(releases)]; rel++ )
66 rel->row = rel->col = EOF;
67
68 /* find lad */
69 for( row = 0; row < DIMROW; row++ )
70 for( s = t = bg[row]; (s = strchr(s,CLAD)); s++ )
71 {
72 /* nasty, check for CLAD's surrounded by CFREEs */
73 if( s[-1] != CFREE || s[1] != CFREE )
74 continue;
75 lad.row = lad.st_row = row;
76 lad.col = lad.st_col = s - t;
77 lad.dir = NONE;
78 lad.jst = 0;
79 bg[lad.row][lad.col] = CFREE;
80 break;
81 }
82
83 /* init ders */
84 if( !ders )
85 {
86 int hi = -1;
87 for( i = 0; i < DIMSCRN; i++ )
88 if( hi < hiders[i] )
89 hi = hiders[i];
90 ders = malloc(sizeof(DER) * (hi + 1));
91 ders[hi].row = EOF;
92 }
93 for( i = 0; i < hiders[scrno]; i++ )
94 {
95 ders[i].launch = i + 1;
96 ders[i].dir = XDOWN;
97 }
98 for( ; ders[i].row != EOF; i++ )
99 ders[i].launch = -1;
100
101 move(LINES - 1, 0);
102 refresh();
103 }
104
reldscreen(void)105 static void reldscreen(void)
106 {
107 int row,i;
108
109 for( row = 0; row < DIMROW; row++ )
110 mvaddstr(row,0,bg[row]);
111
112 /* deal with lad */
113 lad.row = lad.st_row;
114 lad.col = lad.st_col;
115 lad.dir = NONE;
116 lad.jst = 0;
117 mvaddch(lad.row,lad.col,CLAD);
118
119 /* deal with ders */
120 for( i = 0; i < hiders[scrno]; i++ )
121 {
122 ders[i].launch = i + 1;
123 ders[i].dir = XDOWN;
124 }
125
126 move(LINES - 1, 0);
127 refresh();
128 }
129
130 #define SOLID(C) ((C) == CBAR || (C) == CGROUND || (C) == CTRAP1)
131
132 /* drive a single der, tell whether it left the board or hit lad */
drv_der(DER * dp)133 static RESULT drv_der(DER *dp)
134 {
135 #define LorR dchoice[rand() % 2]
136 #define LorRorD dchoice[rand() % 3]
137
138 static DIR dchoice[] = {LEFT,RIGHT,XDOWN};
139 int row = dp->row,
140 col = dp->col;
141 DIR dir = dp->dir;
142 char c;
143
144 c = bg[row][col]; /* restore prev content */
145 mvaddch(row,col,c);
146 if( c == CEXIT )
147 return EXIT;
148 for( ;; )
149 {
150 if( dir == XDOWN )
151 {
152 c = bg[row + 1][col];
153 if( SOLID(c) )
154 {
155 dir = LorR;
156 continue;
157 }
158 row++;
159 break;
160 }
161 if( dir == LEFT )
162 {
163 if( col == 0 || bg[row][col - 1] == CBAR )
164 {
165 dir = RIGHT;
166 continue;
167 }
168 col--;
169 }
170 if( dir == RIGHT )
171 {
172 if( col == DIMCOL - 2 || bg[row][col + 1] == CBAR )
173 {
174 dir = LEFT;
175 continue;
176 }
177 col++;
178 }
179 if( bg[row][col] == CLADDER )
180 dir = LorRorD;
181 else
182 {
183 c = bg[row + 1][col];
184 if( !SOLID(c) )
185 dir = XDOWN;
186 }
187 break;
188 }
189 c = mvinch(row,col);
190 addch(CDER);
191 dp->row = row;
192 dp->col = col;
193 dp->dir = dir;
194 return strchr(laddirs,c) ? DEAD : NORMAL;
195
196 #undef LorR
197 #undef LorRorD
198 }
199
drv_ders(void)200 static RESULT drv_ders(void)
201 {
202 DER *derp;
203 for( derp = ders; derp->row != EOF; derp++ )
204 {
205 if( derp->launch == -1 )
206 continue;
207 if( derp->launch == 0 )
208 {
209 RESULT result = drv_der(derp);
210 if( result == DEAD )
211 return DEAD;
212 if( result == EXIT )
213 derp->launch = 5; /* set new start time */
214 continue;
215 }
216 if( --derp->launch == 0 )
217 /* select a point of release */
218 for( ;; )
219 {
220 int n = rand() % DIM(releases);
221 if( releases[n].row != EOF )
222 {
223 derp->row = releases[n].row;
224 derp->col = releases[n].col;
225 derp->dir = XDOWN;
226 break;
227 }
228 }
229 }
230 return NOTHING_HAPPENED;
231 }
232
lad_died(void)233 static void lad_died(void)
234 {
235 int i,j;
236 static char rot[] = "b+d+q+p+";
237 ctnplay();
238 for( i = 0; i < 5; i++ )
239 for( j = 0; j < DIM(rot) - 1; j++ )
240 {
241 mvaddch(lad.row,lad.col,rot[j]);
242 move(LINES - 1,0);
243 refresh();
244 waitct();
245 }
246 }
247
do_the_hooka(void)248 static void do_the_hooka(void)
249 {
250 for( bonus-- ; bonus >= 0; bonus-- )
251 {
252 add_score(1);
253 stat_bonus();
254 move(DIMROW + 2,0);
255 if( bonus & 1 )
256 addstr("Hooka!");
257 else
258 clrtoeol();
259 move(LINES - 1,0);
260 refresh();
261 waitct();
262 }
263 }
264
pause(void)265 static void pause(void)
266 {
267 mvaddstr(DIMROW + 2,0,"Type RETURN to continue: ");
268 refresh();
269 nodelay(stdscr,FALSE);
270 while( getch() != '\n' )
271 ;
272 nodelay(stdscr,TRUE);
273 move(DIMROW + 2,0);
274 clrtoeol();
275
276 }
277
over_der(int row,int col)278 static void over_der(int row,int col)
279 {
280 /* Funny how lad jumps over "Sc`o're" - avoid it? Na. */
281 if( mvinch(row + 1,col) == CDER || mvinch(row + 2,col) == CDER )
282 add_score(2);
283 }
284
drv_lad(void)285 static RESULT drv_lad(void)
286 {
287 int row = lad.row;
288 int col = lad.col;
289 DIR dir = lad.dir;
290 int jst = lad.jst;
291 char c0,c1;
292 int ch;
293
294 while((ch = getch()) != ERR )
295 {
296 switch(ch)
297 {
298 case ERR: /* no key */
299 break;
300
301 case 'h':
302 case '4':
303 case KEY_LEFT:
304 dir = LEFT;
305 break;
306
307 case 'l':
308 case '6':
309 case KEY_RIGHT:
310 dir = RIGHT;
311 break;
312
313 case 'k':
314 case '8':
315 case KEY_UP:
316 if( !jst )
317 dir = XUP;
318 break;
319
320 case 'j':
321 case '2':
322 case KEY_DOWN:
323 if( !jst )
324 dir = XDOWN;
325 break;
326
327 case ' ':
328 if( !jst ) /* not while we're jumping */
329 jst = 1;
330 break;
331
332 case 'R'-'@':
333 case 'L'-'@':
334 case KEY_CLEAR:
335 wrefresh(curscr);
336 break;
337
338 case '['-'@':
339 return PAUSE;
340
341 case 'C'-'@': /* who does set INTR to ^C, anyway? */
342 for( ; lads >= 1; lads-- )
343 {
344 stat_lads();
345 move(LINES - 1, 0);
346 refresh();
347 waitct();
348 }
349 lads = 1;
350 return DEAD;
351
352 default:
353 dir = STOP;
354 }
355 }
356
357 c0 = bg[row][col];
358 c1 = bg[row + 1][col];
359 if( jst < 2 && !SOLID(c1) && c0 != CLADDER && !(jst == 1 && c0 == CHAZARD) )
360 {
361 /* then fall */
362 jst = 0; /* no request for jumping */
363 row++;
364 }
365 else
366 if( jst >= 1 ) /* request for or within a jump */
367 {
368 if( jst == 1 && c1 == CFREE && c0 != CHAZARD )
369 jst = 0;
370 else
371 {
372 static jra[7] = { 0, -1, -1, 0, 0, 1, 1 };
373 int jc,jr;
374
375 over_der(row,col);
376 if( dir == XUP || dir == XDOWN )
377 dir = STOP;
378 for( ; jst != 7; jst++ )
379 {
380 jr = jra[jst];
381 jc = dir == STOP ? 0 : (dir == LEFT ? -1 : 1);
382 c0 = bg[row + jr][col + jc];
383 if( c0 != CBAR && c0 != CGROUND && !(jr == 1 && c0 == CTRAP1) )
384 {
385 if( (row += jr) < 0 || row > DIMROW - 2 )
386 row -= jr;
387 if( (col += jc) < 0 || col > DIMCOL - 2 )
388 col -= jc;
389 break;
390 }
391 }
392 if( ++jst >= 7 )
393 jst = 0;
394 if( bg[row][col] == CLADDER )
395 {
396 jst = 0;
397 dir = STOP;
398 }
399 if( dir != STOP )
400 over_der(row,col);
401 }
402 }
403 else
404 {
405 if( c1 == CTRAP1 )
406 mvaddch(row + 1,col,bg[row + 1][col] = CFREE);
407 switch( dir )
408 {
409 case LEFT:
410 c1 = bg[row][col - 1];
411 if( col != 0 && c1 != CBAR && c1 != CGROUND )
412 col--;
413 else
414 dir = STOP;
415 break;
416 case RIGHT:
417 c1 = bg[row][col + 1];
418 if( col != DIMCOL - 2 && c1 != CBAR && c1 != CGROUND )
419 col++;
420 else
421 dir = STOP;
422 break;
423 case XUP:
424 if( c0 == CLADDER &&
425 ((c0 = bg[row - 1][col]) == CLADDER || c0 == CTARGET) )
426 row--;
427 else
428 dir = STOP;
429 break;
430 case XDOWN:
431 if( c0 == CLADDER && c1 != CGROUND )
432 row++;
433 else
434 dir = STOP;
435 break;
436 default:
437 break;
438 }
439 }
440
441 if( lad.row != row || lad.col != col || lad.dir != dir || lad.jst != jst )
442 {
443 mvaddch(lad.row,lad.col,bg[lad.row][lad.col]);
444 {
445 /* remove rubbish */
446 static char s[] = {CGOLD,CRELEAS,CLADDER,CTARGET,
447 CEXIT,CBAR,CGROUND,CHAZARD,CTRAP0,CTRAP1,CFREE};
448 if( !strchr(s,bg[row][col]) )
449 mvaddch(row,col,bg[row][col] = CFREE);
450 }
451 /* check for anything that matters */
452 if( bg[row][col] == CGOLD )
453 {
454 mvaddch(row,col,bg[row][col] = CFREE);
455 add_score(bonus);
456 }
457 if( bg[row][col] == CHAZARD )
458 {
459 dir = rand() & 1 ? LEFT : RIGHT;
460 jst = rand() & 1;
461 }
462 lad.row = row;
463 lad.col = col;
464 lad.dir = dir;
465 lad.jst = jst;
466 if( mvinch(row,col) == CDER )
467 return DEAD;
468 addch(laddirs[dir]);
469 }
470 switch( bg[row][col] )
471 {
472 case CTARGET:
473 return FINISH;
474 case CTRAP0:
475 return DEAD;
476 }
477 return NORMAL;
478 }
479
lplay(void)480 RESULT lplay(void)
481 {
482 int tick;
483 RESULT result;
484
485 ldscreen();
486
487 while( lads > 0 )
488 {
489 bonus = boni[scrno];
490 ctplay();
491 stat_lads();
492 stat_level();
493 stat_score();
494 stat_bonus();
495 mvaddstr(DIMROW + 2,0,"Get ready! ");
496 refresh();
497 for( tick = 7; tick; tick-- )
498 waitct();
499 move(DIMROW + 2,0);
500 clrtoeol();
501
502 for( tick = 20 * bonus; tick; tick-- )
503 {
504 if( !((tick - 1) % 20) )
505 {
506 bonus--;
507 stat_bonus();
508 }
509 if( (result = drv_ders()) != DEAD )
510 result = drv_lad();
511 move(LINES - 1,0);
512 refresh();
513 waitct();
514 if( result == PAUSE )
515 pause(),
516 result = NORMAL;
517 if( result != NORMAL )
518 break;
519 }
520 if( !tick )
521 result = DEAD;
522 if( result == DEAD )
523 {
524 lads--;
525 stat_lads();
526 lad_died();
527 }
528 if( result == FINISH )
529 {
530 do_the_hooka();
531 return NORMAL;
532 }
533 reldscreen();
534 }
535 return DEAD;
536 }
537