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