1 /* ztrack - simple curses-based pseudo-3D driving game
2 * public domain by Russell Marks 951025
3 * v1.0 - 951027 - first working version :-)
4 */
5
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <time.h>
12 #include <ncurses.h>
13
14
15 #ifdef HAVE_RANDOM
16 #define rand random
17 #define srand srandom
18 #endif
19
20 #if defined(ACS_HLINE) && !defined(NO_COLOUR) && !defined(NO_COLOR)
21 #define HAVE_COLOUR
22 #endif
23
24
25 /* fixed-point defs */
26 typedef int fixed;
27 #define FIX_UP(x) ((x)<<10) /* move from int to fixed (x1024) */
28 #define FIX_DOWN(x) ((x)>>10) /* move from fixed to int */
29
30
31 /* max other cars
32 * they only exist from 4 'positions' in front to 4 behind */
33 #define MAX_ENEMY 10
34
35 /* since the object of the enemy is to crash into the player,
36 * make their top speed lower.
37 */
38 #define MAX_PLYR_SPEED FIX_UP(150)
39 #define MAX_ENEMY_SPEED FIX_UP(135)
40
41 /* how much we accel/decel per frame */
42 #define PLYR_ACCEL FIX_UP(2)
43 #define PLYR_DECEL FIX_UP(4)
44 #define ENEMY_ACCEL FIX_UP(4)
45 #define ENEMY_DECEL FIX_UP(2)
46
47 /* how much to divide by to turn speeds into amount to add to rel. pos.
48 * basically, lower = faster
49 */
50 #define SPEED_FUDGE 200
51
52 /* response time (in frames) of computer lane-changing */
53 #define ENEMY_RESP_TIME 5 /* half-sec */
54
55
56 /* car 'sprites' in carspr[] */
57 #include "carspr.h"
58
59
60
61 char clouds[3][80];
62
63 int ttyfd,colour=0;
64
65 /* the other cars' positions are given relative to the player.
66 * this saves messing about with a track, etc.
67 * in comments below, all absolute unless specified.
68 */
69 struct enemy_t {
70 int active; /* non-zero if this element in use */
71 int lane; /* lane position */
72 fixed ypos; /* relative position along the road; */
73 /* -ve = behind, +ve = in front, 0 = level. onscreen positions
74 * are FIX_DOWN(ypos)==4,3,2,1. */
75 fixed speed,target; /* current and target speed */
76 int timer,tlane; /* timer must reach 0 before lane=tlane */
77 } enemy[MAX_ENEMY];
78
79
80 /* accel/decel settings */
81 #define SPDMODE_ACCEL 1
82 #define SPDMODE_CONST 0
83 #define SPDMODE_DECEL (-1)
84
85
86
87 /* this is pretty simple at the moment */
use_colour(colnum,other_attr)88 void use_colour(colnum,other_attr)
89 int colnum,other_attr;
90 {
91 #ifdef HAVE_COLOUR
92 if(colnum==0)
93 attrset(A_NORMAL);
94 else
95 if(colour)
96 attrset(COLOR_PAIR(colnum)|other_attr);
97 else
98 attrset(other_attr);
99 #endif
100 }
101
102
clearscreen()103 void clearscreen()
104 {
105 use_colour(COLOR_WHITE,A_NORMAL); /* just in case */
106 wclear(stdscr);
107 move(0,0); refresh();
108 }
109
110
screenon()111 void screenon()
112 {
113 initscr();
114 cbreak(); noecho();
115 if((ttyfd=open("/dev/tty",O_RDONLY|O_NONBLOCK))<0)
116 return;
117 }
118
119
screenoff()120 void screenoff()
121 {
122 close(ttyfd);
123 clearscreen();
124 echo(); nocbreak();
125 endwin();
126 putchar('\n');
127 }
128
129
getnbkey()130 int getnbkey()
131 {
132 unsigned char c;
133
134 if(read(ttyfd,&c,1)==-1)
135 return(-1);
136 else
137 return((int)c);
138 }
139
140
randomize()141 void randomize()
142 {
143 srand(time(NULL));
144 }
145
146
define_colours()147 void define_colours()
148 {
149 #ifndef HAVE_COLOUR
150 /* this is a nop without colour support */
151 #else
152 int f;
153
154 /* make it use colour */
155 start_color();
156
157 colour=has_colors();
158
159 if(!colour) return;
160
161 /* declare colour pairs we need */
162 /* we do it like this; every colour available is allocated to
163 * the colour_number pair, with black as the background.
164 * (black on black is pointless and isn't defined)
165 * then the use_colour routine can be used;
166 * use_colour(COLOR_RED,A_BOLD);
167 * would give bright red. attrset(A_NORMAL) should be used to
168 * reset to normal text.
169 *
170 * we assume the colours are defined to 1..7 which is losing but
171 * makes things really easy. :-)
172 */
173
174 for(f=1;f<=7;f++)
175 init_pair(f,f,0);
176 #endif
177 }
178
179
180
init()181 void init()
182 {
183 randomize();
184
185 /* init clouds, these are only setup once */
186 strcpy(clouds[0],
187 "_,,/////////////,,,,____ \
188 ,,,////////////, ,, ");
189 strcpy(clouds[1],
190 " '' '' ---=======---- \
191 ' '''' ' ");
192 strcpy(clouds[2],
193 " ---==========----____,,,----- \
194 ----============-- ");
195
196 use_colour(COLOR_CYAN,A_NORMAL);
197
198 mvaddstr(0,0,clouds[0]);
199 mvaddstr(1,0,clouds[1]);
200 mvaddstr(2,0,clouds[2]);
201 }
202
203
stashcursor()204 void stashcursor()
205 {
206 move(23,0);
207 }
208
209
drawscrn()210 void drawscrn()
211 {
212 stashcursor();
213 refresh();
214 }
215
216
217 /* draw landscape depending on whether in lane 0, 1 or 2 */
landscape(int lane)218 void landscape(int lane)
219 {
220 int f,x;
221
222 use_colour(COLOR_GREEN,A_NORMAL);
223
224 /* reset line 3 to spaces and 4 to underscores */
225 move(3,0); for(f=0;f<79;f++) addch(' ');
226 move(4,0); for(f=0;f<79;f++) addch('_');
227
228 /* now draw in the 'landscape' with a suitable offset */
229 x=10-(lane-1)*3;
230 mvaddstr(3,x+11,"___");
231 mvaddstr(4,x,",-^---._,-' ^^--..__________,._______________..------..");
232 }
233
234
235 /* clear main part of the screen, where the track and cars are drawn */
clear_main()236 void clear_main()
237 {
238 int f;
239
240 for(f=5;f<21;f++)
241 move(f,0),clrtoeol();
242 }
243
244
mycar(int damaged)245 void mycar(int damaged)
246 {
247 use_colour(COLOR_MAGENTA,A_BOLD);
248
249 if(damaged) /* non-zero if car has crashed */
250 {
251 mvaddstr(21,20," / _ __ \\ ");
252 mvaddstr(22,20,"/ ,' `-~~~'`-__/^---__,-^ ^--. \\");
253 /* also crack wing mirrors */
254 use_colour(COLOR_YELLOW,A_BOLD);
255 mvaddstr(22,10,"[-xXXx-]");
256 mvaddstr(22,61,"[-xXXx-]");
257 }
258 else
259 {
260 mvaddstr(21,20," / _________________________ \\ ");
261 mvaddstr(22,20,"/ ,' `. \\");
262 }
263 }
264
265
draw_lanes(int lane)266 void draw_lanes(int lane)
267 {
268 int x,y;
269
270 use_colour(COLOR_MAGENTA,A_NORMAL);
271
272 if(lane==2)
273 {
274 /* border 1, leftmost */
275 mvaddch(10,0,'\'');
276 for(y=9,x=1;y>=5;y--,x+=6)
277 mvaddstr(y,x,"__---'");
278 }
279
280 if(lane>=1)
281 {
282 /* border 2 */
283 mvaddstr(16,0,"-'");
284 for(y=15,x=2;y>=5;y--,x+=3)
285 mvaddstr(y,x,"_-'");
286 }
287
288 /* borders 3 and 4, middle two, always drawn */
289 use_colour(COLOR_MAGENTA,A_BOLD);
290 for(y=22,x=20;y>=5;y--,x++)
291 mvaddch(y,x,'/');
292 for(y=22,x=58;y>=5;y--,x--)
293 mvaddch(y,x,'\\');
294
295 use_colour(COLOR_MAGENTA,A_NORMAL);
296
297 if(lane<=1)
298 {
299 /* border 5 */
300 mvaddstr(16,77,"`-");
301 for(y=15,x=74;y>=5;y--,x-=3)
302 mvaddstr(y,x,"`-_");
303 }
304
305 if(lane==0)
306 {
307 /* border 6, rightmost*/
308 mvaddch(10,78,'`');
309 for(y=9,x=72;y>=5;y--,x-=6)
310 mvaddstr(y,x,"`---__");
311 }
312 }
313
314
315 /* init enemy car array - just blank it */
init_enemies()316 void init_enemies()
317 {
318 int f;
319
320 for(f=0;f<MAX_ENEMY;f++) enemy[f].active=0;
321 }
322
323
324 /* draw sprite from carspr */
draw_car_sprite(int x,int y)325 void draw_car_sprite(int x,int y) /* x,y pos in carspr[] array */
326 {
327 int a,b,ox,oy,lastx;
328 struct carspr_t *cp;
329 char *ptr;
330
331 use_colour(COLOR_YELLOW,(y>1)?A_BOLD:A_NORMAL);
332
333 cp=&carspr[y][x];
334
335 for(b=0,oy=cp->y;b<MAX_SPR_Y;b++,oy++)
336 {
337 ptr=cp->d[b];
338 move(oy,cp->x);
339 for(a=0,lastx=ox=cp->x;a<MAX_SPR_X;a++,ox++,ptr++)
340 if(*ptr!=32)
341 {
342 /* we optimise cursor movement by hand in case curses is a bit dim */
343 if(lastx!=ox) move(oy,ox),lastx=ox;
344 addch(*ptr);
345 lastx++;
346 }
347 }
348 }
349
350
351 /* draw car, if possible */
draw_car(struct enemy_t * car,int lane)352 void draw_car(struct enemy_t *car,int lane)
353 {
354 int x,y;
355
356 if(!car->active) return;
357 y=FIX_DOWN(car->ypos);
358 if(y<1 || y>4) return;
359
360 x=car->lane-lane;
361
362 /* if we got here, we can draw it. */
363 draw_car_sprite(x+2,4-y);
364 }
365
366
367 /* returns non-zero if enemy at stated pos.
368 * the ypos is rounded with FIX_DOWN(), like it is for onscreen pos.
369 */
enemy_at(fixed ypos,int lane)370 int enemy_at(fixed ypos,int lane)
371 {
372 int f,y=FIX_DOWN(ypos);
373
374 for(f=0;f<MAX_ENEMY;f++)
375 if(enemy[f].active)
376 if(enemy[f].lane==lane && FIX_DOWN(enemy[f].ypos)==y)
377 return(1);
378
379 return(0);
380 }
381
382
create_enemy(fixed plyr_speed)383 void create_enemy(fixed plyr_speed)
384 {
385 int f,tmp;
386
387 /* find a blank space in the array */
388 for(f=0;f<MAX_ENEMY;f++)
389 if(!enemy[f].active)
390 {
391 enemy[f].lane=rand()%3;
392 enemy[f].timer=0;
393 tmp=rand()%MAX_ENEMY_SPEED;
394 if(tmp<70) tmp=70;
395 enemy[f].speed=enemy[f].target=tmp;
396 /* if we're going faster than player, start us behind them,
397 * else in front.
398 */
399 enemy[f].ypos=(enemy[f].speed>plyr_speed)?FIX_UP(-4):FIX_UP(4);
400 /* if there's one where we want to be, forget it */
401 if(enemy_at(enemy[f].ypos,enemy[f].lane)) return;
402 /* otherwise, it's now valid */
403 enemy[f].active=1;
404 return;
405 }
406 }
407
408
409 /* do all the enemy stuff
410 * return value is whether player tail-gated a car this go or not
411 * (that's the only collision which is allowed)
412 */
do_enemy(int plyr_lane,fixed plyr_speed)413 int do_enemy(int plyr_lane,fixed plyr_speed)
414 {
415 int f,tmp;
416
417 /* first, correct relative positions according to player speed
418 * and enemy speed.
419 */
420 for(f=0;f<MAX_ENEMY;f++)
421 if(enemy[f].active)
422 enemy[f].ypos+=(enemy[f].speed - plyr_speed)/SPEED_FUDGE;
423
424 /* drop any which have fallen off the edge of the world */
425 for(f=0;f<MAX_ENEMY;f++)
426 if(enemy[f].active)
427 {
428 if(enemy[f].ypos>=FIX_UP(5) || enemy[f].ypos<=FIX_UP(-5))
429 enemy[f].active=0;
430 }
431
432 /* possibly create a new one */
433 /* want to make it more likely with increased plyr speed...? */
434 if(rand()%7==0) create_enemy(plyr_speed);
435
436 /* now let them make their move */
437 for(f=0;f<MAX_ENEMY;f++)
438 if(enemy[f].active)
439 {
440 /* countdown lane-change response time timeout */
441 if(enemy[f].timer)
442 {
443 enemy[f].timer--;
444 if(enemy[f].timer==0)
445 {
446 /* don't allow if some enemy already there or if player there */
447 if(!enemy_at(enemy[f].ypos,enemy[f].tlane) &&
448 FIX_DOWN(enemy[f].ypos)!=0)
449 enemy[f].lane=enemy[f].tlane;
450 }
451 }
452
453 /* if we're behind player and in same lane, change */
454 if(enemy[f].ypos<=FIX_UP(-1) && enemy[f].lane==plyr_lane &&
455 enemy[f].timer==0)
456 {
457 tmp=(enemy[f].lane+1)%3;
458 if(rand()&1) tmp=(enemy[f].lane+4)%3; /* i.e. lane-1 mod 3 */
459 enemy[f].timer=ENEMY_RESP_TIME;
460 enemy[f].tlane=tmp;
461 }
462
463 #if 0 /* this is really seriously crap */
464 /* now target speed stuff. */
465 /* if (far) behind, aim for player speed + 10. */
466 if(enemy[f].ypos<FIX_UP(-2)) enemy[f].target=plyr_speed+FIX_UP(10);
467
468 /* if (far) in front, aim for player speed. */
469 if(enemy[f].ypos>FIX_UP( 2)) enemy[f].target=plyr_speed;
470 #endif
471 }
472
473 #if 0 /* not needed for now */
474 /* do speed add/sub to aim for target */
475 for(f=0;f<MAX_ENEMY;f++)
476 if(enemy[f].active && enemy[f].speed!=enemy[f].target)
477 {
478 tmp=enemy[f].target-enemy[f].speed;
479 if(tmp>0)
480 {
481 /* too slow, accelerate */
482 if(tmp>ENEMY_ACCEL) tmp=ENEMY_ACCEL;
483 enemy[f].speed+=tmp;
484 }
485 else
486 {
487 /* too fast, decelerate */
488 tmp=-tmp;
489 if(tmp>ENEMY_DECEL) tmp=ENEMY_DECEL;
490 enemy[f].speed-=tmp;
491 }
492 }
493 #endif
494
495 /* check for 'impossible' things we need to fix */
496 for(f=0;f<MAX_ENEMY;f++)
497 if(enemy[f].active)
498 {
499 /* if they've run into the back of the player, push them back */
500 if(enemy[f].ypos>=0 && enemy[f].ypos<FIX_UP(1) &&
501 enemy[f].lane==plyr_lane && enemy[f].speed>plyr_speed)
502 enemy[f].ypos=FIX_UP(-1);
503 /* XXX that can result in two enemy on same spot :-( */
504 }
505
506 /* draw any cars onscreen */
507 for(f=0;f<MAX_ENEMY;f++)
508 if(enemy[f].active)
509 draw_car(&enemy[f],plyr_lane);
510
511
512 /* see if a car hit the player from the front.
513 * we interpret this as FIX_DOWN(ypos)==0 and enemy speed < player speed.
514 * and same lane, of course. :-)
515 * this MUST be the last bit in the routine, as it may return.
516 */
517 for(f=0;f<MAX_ENEMY;f++)
518 if(enemy[f].active)
519 {
520 if(FIX_DOWN(enemy[f].ypos)==0 && enemy[f].speed<plyr_speed &&
521 enemy[f].lane==plyr_lane)
522 return(1);
523 }
524
525 return(0);
526 }
527
528
529
530 /* test sprites */
test()531 void test()
532 {
533 int a,b;
534
535 clear_main();
536 landscape(1);
537
538 for(a=0;a<3;a++) draw_lanes(a);
539
540 for(b=0;b<4;b++)
541 for(a=0;a<5;a++)
542 draw_car_sprite(a,b);
543
544 drawscrn();
545 while(getnbkey()==-1) usleep(100000);
546 }
547
548
draw_mirrors(int lane)549 void draw_mirrors(int lane)
550 {
551 static char mir_none[]="[ ]",mir_car[]="[ .##. ]";
552 int f,l=0,r=0;
553
554 for(f=0;f<MAX_ENEMY;f++)
555 if(enemy[f].active)
556 if(FIX_DOWN(enemy[f].ypos)==0)
557 if(enemy[f].lane==lane-1)
558 l=1;
559 else
560 if(enemy[f].lane==lane+1)
561 r=1;
562
563 use_colour(COLOR_YELLOW,A_BOLD);
564
565 mvaddstr(22,10,l?mir_car:mir_none);
566 mvaddstr(22,61,r?mir_car:mir_none);
567 }
568
569
570
571 #ifdef DEBUG
draw_map(int lane)572 void draw_map(int lane)
573 {
574 int y,x,f,yp;
575
576 use_colour(COLOR_WHITE,A_BOLD);
577
578 for(y=0;y<9;y++)
579 for(x=0;x<3;x++)
580 mvaddch(y,x,'.');
581
582 for(f=0;f<MAX_ENEMY;f++)
583 if(enemy[f].active && (yp=FIX_DOWN(enemy[f].ypos))<=4 && yp>=-4)
584 mvaddch(4-yp,enemy[f].lane,'x');
585
586 mvaddch(4,lane,'*');
587 }
588 #endif
589
590
draw_status(int score,int lives,fixed speed,int spd_mode)591 void draw_status(int score,int lives,fixed speed,int spd_mode)
592 {
593 use_colour(COLOR_CYAN,A_NORMAL);
594 mvprintw(23,12,"< score: %08d cars: %d speed: %3d mph [%s] >",
595 score,lives,FIX_DOWN(speed),
596 (spd_mode==SPDMODE_CONST)?"const":
597 (spd_mode==SPDMODE_ACCEL)?"accel":"decel");
598 }
599
600
601 /* 'scroll' string right. assumes len>=2 */
rscroll(char * str)602 void rscroll(char *str)
603 {
604 int len=strlen(str);
605 char tmp;
606
607 tmp=str[len-1];
608 memmove(str+1,str,len-1);
609 *str=tmp;
610 }
611
612
move_clouds()613 void move_clouds()
614 {
615 static int move3rd=0;
616
617 use_colour(COLOR_CYAN,A_NORMAL);
618
619 rscroll(clouds[0]); mvaddstr(0,0,clouds[0]);
620 rscroll(clouds[1]); mvaddstr(1,0,clouds[1]);
621
622 if(move3rd) rscroll(clouds[2]),mvaddstr(2,0,clouds[2]);
623
624 move3rd=!move3rd;
625 }
626
627
lose_input()628 void lose_input()
629 {
630 while(getnbkey()!=-1);
631 }
632
633
634
635 /* returns final score */
game()636 int game()
637 {
638 int lane,plyr_hit,f;
639 unsigned int frames=0;
640 int quit=0;
641 int spd_mode;
642 fixed speed=0;
643 int score=0;
644 int lives=3;
645
646 /* setup initial screen */
647 clearscreen();
648 mycar(0);
649 move_clouds(); /* to make sure they're drawn */
650 move_clouds();
651
652 while(!quit && lives>0)
653 {
654 plyr_hit=0;
655 speed=0; spd_mode=SPDMODE_CONST;
656 init_enemies();
657 lane=1;
658
659 /* put a couple of cars about */
660 for(f=0;f<3;f++) create_enemy(speed);
661
662 lose_input();
663
664 while(!quit && !plyr_hit)
665 {
666 switch(getnbkey())
667 {
668 case 'q': spd_mode=SPDMODE_ACCEL; break;
669 case 'a': spd_mode=SPDMODE_DECEL; break;
670 case 32:
671 case 9: spd_mode=SPDMODE_CONST; break; /* 9 = ^I = TAB */
672 case 'o': if(lane>0) lane--; break;
673 case 'p': if(lane<2) lane++; break;
674
675 case 'X': case 27: quit=1;
676 }
677
678 /* deal with remaining player movement */
679 switch(spd_mode)
680 {
681 case SPDMODE_ACCEL:
682 speed+=PLYR_ACCEL; break;
683 case SPDMODE_DECEL:
684 speed-=PLYR_DECEL; break;
685 /* as no 'friction', SPDMODE_CONST is a nop */
686 }
687
688 if(speed<0) speed=0;
689 if(speed>MAX_PLYR_SPEED) speed=MAX_PLYR_SPEED;
690
691 /* draw screen except enemy cars */
692 landscape(lane);
693 clear_main();
694 draw_lanes(lane);
695
696 /* fix enemy positions relative, do their move, draw and check hit */
697 plyr_hit=do_enemy(lane,speed);
698 draw_mirrors(lane);
699
700 #ifdef DEBUG
701 draw_map(lane);
702 #endif
703
704 draw_status(score,lives,speed,spd_mode);
705 if(frames%20==19) move_clouds();
706
707 drawscrn();
708
709 /* don't wait if hit; it looks odd */
710 if(!plyr_hit) usleep(100000);
711 frames++;
712 score+=(FIX_DOWN(speed)/5);
713 }
714
715 if(plyr_hit)
716 {
717 lives--;
718 draw_status(score,lives,0,spd_mode);
719 mycar(1); /* show crash */
720 use_colour(COLOR_YELLOW,A_BOLD);
721 mvaddstr(10,30,",------------------.");
722 mvaddstr(11,30,"| CRASH! LIFE LOST |");
723 mvaddstr(12,30,"`------------------'");
724 lose_input(); drawscrn();
725 sleep(2);
726 if(lives==0)
727 {
728 mvaddstr(11,32,"G A M E O V E R");
729 lose_input(); drawscrn();
730 sleep(2);
731 }
732 else
733 mycar(0);
734 }
735 }
736
737 return(score);
738 }
739
740
741 /* returns non-zero if want to quit */
title(int high)742 int title(int high)
743 {
744 static char logo[7][80]={
745 " ________________________________"
746 "_ _ ,,,.. _______ __ __ ",
747 " /________ ____ ____ __ "
748 "/ --==,' \\===''~ / ____/ / / ,' / ",
749 "----=======- ,-' ,-' / / / /_/ /"
750 " ,' \\ / / / /,' ,' ",
751 "_________ ,-' ,-'____ / /__ / ___/_"
752 "_ ,' ,'\\ \\ _ / /____ / ,'_______",
753 "%%%%%%%,-' ,-'%%%%%% / /%% / . \\%%%%"
754 ",' '--` \\%/ /%%%%%/ /\\ \\%%%%%%%%",
755 "::::,-' ,-'_______::/ /:::/ /:\\ \\_,'"
756 " ,-------. ` /_____/ /::\\ \\:::::::",
757 ".../______________/./__/.../__/...\\_____"
758 ",'.........`.___________/....\\__\\......"};
759
760 int c;
761
762 clearscreen();
763
764 /* draw logo */
765 use_colour(COLOR_CYAN,A_NORMAL);
766 mvaddstr(0,0,logo[0]);
767 use_colour(COLOR_CYAN,A_BOLD);
768 mvaddstr(1,0,logo[1]);
769 use_colour(COLOR_YELLOW,A_BOLD);
770 mvaddstr(2,0,logo[2]);
771 mvaddstr(3,0,logo[3]);
772 use_colour(COLOR_MAGENTA,A_BOLD);
773 mvaddstr(4,0,logo[4]);
774 use_colour(COLOR_RED,A_BOLD);
775 mvaddstr(5,0,logo[5]);
776 use_colour(COLOR_GREEN,A_NORMAL);
777 mvaddstr(6,0,logo[6]);
778 use_colour(COLOR_CYAN,A_NORMAL);
779 mvaddstr(7,0,logo[7]);
780
781 use_colour(COLOR_GREEN,A_BOLD);
782 mvaddstr(10,18,"Press:");
783 use_colour(COLOR_YELLOW,A_BOLD);
784 mvaddstr(13,30,"[Space] Play");
785 mvaddstr(15,30,"[Esc] Quit");
786
787 use_colour(COLOR_CYAN,A_NORMAL);
788 mvaddstr(21,5,
789 "Keys: Q = accel mode A = decel mode Space = const (cruise) mode");
790 use_colour(COLOR_GREEN,A_NORMAL);
791 mvaddstr(22,5,
792 " O = move left a lane P = move right a lane Esc = quit");
793
794 use_colour(COLOR_GREEN,A_BOLD);
795 mvprintw(18,24,"Current high score: %08d",high);
796
797 drawscrn();
798
799 while((c=getnbkey())!=' ' && c!='q' && c!=27)
800 usleep(200000);
801
802 return(c==' '?0:1);
803 }
804
805
806
main()807 int main()
808 {
809 int quit=0;
810 int score,high=0;
811
812 screenon();
813 define_colours();
814 init();
815
816 while(!quit)
817 {
818 quit=title(high);
819 if(!quit)
820 {
821 score=game();
822 if(score>high) high=score;
823 }
824 }
825
826 screenoff();
827 exit(0);
828 }
829