home *** CD-ROM | disk | FTP | other *** search
/ Borland Programmer's Resource / Borland_Programmers_Resource_CD_1995.iso / code / bcpp / file8 / gravity.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-19  |  50.2 KB  |  1,268 lines

  1. #include <graphics.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <conio.h>
  5. #include <math.h>
  6. #include <time.h>
  7. #include <ctype.h>
  8. #include <dos.h>
  9. #include <string.h>
  10. #include "def.h"
  11.  
  12. #include "gravity.h"
  13.  
  14. UBYTE bnw= FALSE ;  /* global black & white flag */
  15.  
  16. /************************************************************************/
  17. /*                                                                      */
  18. /* This is the main routine.                                            */
  19. /* Here we start the random number generator, initialize everything and */
  20. /* execute the main user input loop.  As soon as there are less than 2  */
  21. /* players alive we exit to DOS.  Overall, a very boring main program.  */
  22. /*                                                                      */
  23. /*  Inputs : None                                                       */
  24. /*                                                                      */
  25. /*    I/Os : None                                                       */
  26. /*                                                                      */
  27. /* Globals : (BELOW ARE ALL GLOBALS, NONE USED DIRECTLY BY main() )     */
  28. /*           maxx    - Max X allowable coordinate for graphics          */
  29. /*           maxy    - Max Y allowable coordiante for graphics          */
  30. /*                     (Actual Max Y minus text window size)            */
  31. /*           planets - the planet array                                 */
  32. /*           players - the player array                                 */
  33. /*                                                                      */
  34. /* Returns : Error code (if any)                                        */
  35. /*                                                                      */
  36. /* Version : 1.00 (11/15/92)                                            */
  37. /*           1.10 (11/22/92) Now supports seven players                 */
  38. /*                           Compass around player inputting data       */
  39. /*                           Planets blow up when hit 3x                */
  40. /*           1.20 (12/25/92) Planets blow up when hit 4x now            */
  41. /*                           Planets re-appear after 3 turns            */
  42. /*                           Players re-appear after 3 turns            */
  43. /*                           Added opening & closing messages           */
  44. /*                           Dynamic player addition/deletion added     */
  45. /*                           Added (sortof) black & white support       */
  46. /*                                                                      */
  47. /*           (c) 1992 CMI Productions                                   */
  48. /*           15014 East Idaho Place                                     */
  49. /*           Aurora, CO 80012                                           */
  50. /*           (303) 755-8006                                             */
  51. /*                                                                      */
  52. /*           Public Domain Software.  You are free to distribute &      */
  53. /*           modify, as long as above copyright message trailing        */
  54. /*           program message are left intact.                           */
  55. /*                                                                      */
  56. /************************************************************************/
  57. void main(int argc, char *argv[])
  58. {
  59.  
  60.   /* see if user asked for black&white mode */
  61.   if (argc==2)
  62.   {
  63.     if (!stricmp(argv[1],"bw"))
  64.     {
  65.       bnw = TRUE ;
  66.     }
  67.   }
  68.  
  69.   /* start off randomly */
  70.   randomize() ;
  71.  
  72.   /* start graphics mode, place the players & planets */
  73.   init_screen() ;
  74.   get_planets() ;
  75.   get_players() ;
  76.   paint_screen() ;
  77.  
  78.   /* the main input loop */
  79.   do
  80.   {
  81.     /* get the users new projectile directions, abort if requested */
  82.     if (get_inputs())
  83.     {
  84.       break ;
  85.     }
  86.  
  87.     /* repaint the screen, erasing the old projectile paths */
  88.     paint_screen() ;
  89.     /* show the new projectile paths */
  90.     show_paths() ;
  91.     /* let people read last status message */
  92.     delay (2000) ;
  93.   }
  94.   /* do this while there are at least two players */
  95.   while (players_left() >1) ;
  96.  
  97.   /* state the obvious */
  98.   msg (RED,"GAME OVER!") ;
  99.   delay (5000) ;
  100.  
  101.   /* and exit  */
  102.   closegraph();
  103.   printf("This has been yet another CMI Production.\n\n") ;
  104. }
  105.  
  106. /************************************************************************/
  107. /*                                                                      */
  108. /* This routine gets us into graphics mode and sets up the basics.      */
  109. /* Before getting into graphics mode we register our driver and font so */
  110. /* those unfortunate souls withoug BC++ (and the BGI drivers) can also  */
  111. /* play the game.  This game is set up only for VGA, so it is specific- */
  112. /* ally requested.  After getting into graphics mode we get the max     */
  113. /* allowable coordinates, draw a border and select the font.            */
  114. /*                                                                      */
  115. /* Note that this routine assumes that EGAVGA.OBJ and LITT.OBJ are      */
  116. /* being linked in at compile time!                                     */
  117. /*                                                                      */
  118. /*  Inputs : None                                                       */
  119. /*                                                                      */
  120. /*    I/Os : None                                                       */
  121. /*                                                                      */
  122. /* Globals : maxx - playing board max x dimension                       */
  123. /*           maxy - playing board max y dimension                       */
  124. /*                                                                      */
  125. /* Returns : Nothing                                                    */
  126. /*                                                                      */
  127. /************************************************************************/
  128. void init_screen (void)
  129. {
  130.   WORD gdriver, gmode, errorcode;
  131.   UBYTE i ;
  132.  
  133.   /* make sure we regsiter the VGA driver at compile time */
  134.   if (registerbgidriver(EGAVGA_driver) < 0) exit(1) ;
  135.   if (registerbgifont(small_font) < 0) exit(1) ;
  136.  
  137.   /* the display and mode we want */
  138.   gdriver = VGA   ;
  139.   gmode   = VGAHI ;
  140.  
  141.   /* initialize graphics mode */
  142.   initgraph(&gdriver, &gmode, "");
  143.  
  144.   /* read result of initialization */
  145.   errorcode = graphresult();
  146.  
  147.   if (errorcode != grOk)  /* an error occurred */
  148.   {
  149.     printf("Graphics error: %s\n", grapherrormsg(errorcode));
  150.     exit(1);
  151.   }
  152.  
  153.   /* get screen limits (decrease Y for text input area) */
  154.   maxx = getmaxx() ;
  155.   maxy = getmaxy()-20 ;
  156.  
  157.   /* draw the border */
  158.   moveto(0   ,0   ) ;
  159.   lineto(0   ,maxy) ;
  160.   lineto(maxx,maxy) ;
  161.   lineto(maxx,0   ) ;
  162.   lineto(0   ,0   ) ;
  163.  
  164.   settextstyle(SMALL_FONT, HORIZ_DIR, 30);
  165.   setcolor(RED) ;
  166.  
  167.   for (i=0;i<5;i++)
  168.   {
  169.     outtextxy(150+i,100-i,"GRAVITY WARS") ;
  170.     outtextxy(200+i,150-i,"Ver. 1.20") ;
  171.   }
  172.  
  173.   settextstyle(SMALL_FONT, HORIZ_DIR, 5);
  174.  
  175. }
  176.  
  177. /************************************************************************/
  178. /*                                                                      */
  179. /* This routine prints a message in the message area.                   */
  180. /* We first erase anything that may have been there, select the desired */
  181. /* color, and finally print the string.                                 */
  182. /*                                                                      */
  183. /*  Inputs : color - the color to print the text                        */
  184. /*           *line - the text to print                                  */
  185. /*                                                                      */
  186. /*    I/Os : None                                                       */
  187. /*                                                                      */
  188. /* Globals : None                                                       */
  189. /*                                                                      */
  190. /* Returns : Nothing                                                    */
  191. /*                                                                      */
  192. /************************************************************************/
  193. void msg (UBYTE color,char *line)
  194. {
  195.   /* fill the text rectangle area with black (erase) */
  196.   setfillstyle (SOLID_FILL,BLACK) ;
  197.   bar(0,maxy+1,maxx,maxy+21) ;
  198.  
  199.   /* select the color and print the text */
  200.   setcolor(color) ;
  201.   outtextxy(0,maxy,line) ;
  202. }
  203.  
  204. /************************************************************************/
  205. /*                                                                      */
  206. /* This routine creates all the planets for the game.                   */
  207. /* First we initialize all the planets and players to "dead".  This is  */
  208. /* because the create_planet routine makes sure planets don't overlap   */
  209. /* any previously existing ones.  Since these are the first planets we  */
  210. /* must make sure planets can go anywhere.                              */
  211. /* We do this by creating a random set of specs for the planet and then */
  212. /* make sure they don't conflict with another already existing planet.  */
  213. /*                                                                      */
  214. /*  Inputs : None                                                       */
  215. /*                                                                      */
  216. /*    I/Os : None                                                       */
  217. /*                                                                      */
  218. /* Globals : planets - planetery database array                         */
  219. /*           players - player database array                            */
  220. /*                                                                      */
  221. /* Returns : Nothing                                                    */
  222. /*                                                                      */
  223. /************************************************************************/
  224. void get_planets(void)
  225. {
  226.   UBYTE i ; /* general array index */
  227.  
  228.   /* initialize all the planets to "dead" */
  229.   for (i=0;i<MAX_PLANETS;i++)
  230.   {
  231.     planets[i].outs = 1 ;
  232.   }
  233.  
  234.   /* initialize all the players to "dead" */
  235.   for (i=0;i<MAX_PLAYERS;i++)
  236.   {
  237.     players[i].alive = FALSE ;
  238.     players[i].outs = 0 ;
  239.   }
  240.  
  241.   /* finally create all the planets */
  242.   for (i=0;i<MAX_PLANETS;i++)
  243.   {
  244.     create_planet (i) ;
  245.   }
  246.  
  247. }  /* function get_planets */
  248.  
  249.  
  250. /************************************************************************/
  251. /*                                                                      */
  252. /* This routine
  253. /* Pretty standard math here...                                         */
  254. /*                                                                      */
  255. /*  Inputs : i - the planetary index to create                          */
  256. /*                                                                      */
  257. /*    I/Os : None                                                       */
  258. /*                                                                      */
  259. /* Globals : planets - planetery database array                         */
  260. /*           players - player database array                            */
  261. /*                                                                      */
  262. /* Returns : None                                                       */
  263. /*                                                                      */
  264. /************************************************************************/
  265.  
  266. void create_planet(UBYTE i)
  267. {
  268.   UBYTE j                            ; /* general array index */
  269.   UWORD lradius,ldensity,lorgx,lorgy ; /* local (temporary) planetary data */
  270.   float lmass                        ; /* local planetary mass */
  271.   UBYTE conflict                     ; /* planetary conflict flag */
  272.  
  273.   do /* until planet doesn't overlap another */
  274.   {
  275.     /* guess some planetary data */
  276.     ldensity = random(4)+1 ;
  277.  
  278.     /* if it is a high density body, it can only be small */
  279.     if (ldensity!=4)
  280.     {
  281.       lradius = random(45)+10 ;
  282.     }
  283.     else
  284.     {
  285.       lradius  = 5 ;
  286.     }
  287.  
  288.     /* compute the mass based on density and size */
  289.     lmass = (lradius/2+2)*(ldensity*2-1)*10 ;
  290.  
  291.     /* planets are at least 5 pixels from the edge of the screen */
  292.     lorgx = lradius+5+random(maxx-2*lradius-10) ;
  293.     lorgy = lradius+5+random(maxy-2*lradius-10) ;
  294.  
  295.     /* start out with no conflicts */
  296.     conflict = FALSE ;
  297.  
  298.     /* for all possible planets... */
  299.     for(j=0;j<MAX_PLANETS;j++)
  300.     {
  301.       /* if the planet exists... */
  302.       if (planets[j].outs==0)
  303.       {
  304.         /* planets must be at least 100 pixels apart, surface-to-surface */
  305.         if (dist(lorgx,lorgy,planets[j].orgx,planets[j].orgy) < (lradius+planets[j].radius+100))
  306.         {
  307.           conflict = TRUE ;
  308.         } /* if conflict */
  309.       }   /* for existing planets */
  310.     }     /* for all possible planets */
  311.  
  312.     /* for all possible players... */
  313.     for(j=0;j<MAX_PLAYERS;j++)
  314.     {
  315.       /* if the playeer exists... */
  316.       if (players[j].outs==0)
  317.       {
  318.         /* planets must be at least 20 pixels from nearest player */
  319.         if (dist(lorgx,lorgy,players[j].orgx,players[j].orgy) < (lradius+20))
  320.         {
  321.           conflict = TRUE ;
  322.         } /* if conflict */
  323.       }   /* for existing planets */
  324.     }     /* for all possible planets */
  325.  
  326.   }
  327.   while (conflict) ;
  328.   /* put the temporary values in the planetary data array */
  329.   planets[i].radius  = lradius  ;
  330.   planets[i].density = ldensity ;
  331.   planets[i].orgx    = lorgx    ;
  332.   planets[i].orgy    = lorgy    ;
  333.   planets[i].mass    = lmass    ;
  334.   planets[i].hits    = 0        ;
  335.   planets[i].outs    = 0        ;
  336.  
  337. } /* function create_planet */
  338.  
  339. /************************************************************************/
  340. /*                                                                      */
  341. /* This routine computes the distance between two points.               */
  342. /* Pretty standard math here...                                         */
  343. /*                                                                      */
  344. /*  Inputs : x1,y1 - first coordinate pair                              */
  345. /*           x2,y2 - second coordinate pair                             */
  346. /*                                                                      */
  347. /*    I/Os : None                                                       */
  348. /*                                                                      */
  349. /* Globals : None                                                       */
  350. /*                                                                      */
  351. /* Returns : integer distance                                           */
  352. /*                                                                      */
  353. /************************************************************************/
  354. UWORD dist (UWORD x1,UWORD y1,UWORD x2,UWORD y2)
  355. {
  356.   float fdist ; /* floating point distance first */
  357.   UWORD ldist ; /* integer version */
  358.  
  359.   /* if you don't understand this, better quit! */
  360.   fdist = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)) ;
  361.   ldist = fdist ;
  362.   return (ldist) ;
  363. }
  364.  
  365. /************************************************************************/
  366. /*                                                                      */
  367. /* This routine figgers out where to put all the players.               */
  368. /* We guess a random place for the player then make sure they are not   */
  369. /* on top of a planet or other player.                                  */
  370. /*                                                                      */
  371. /*  Inputs : None                                                       */
  372. /*                                                                      */
  373. /*    I/Os : None                                                       */
  374. /*                                                                      */
  375. /* Globals : players - player database array                            */
  376. /*           planets - planetary database array                         */
  377. /*                                                                      */
  378. /* Returns : Nothing                                                    */
  379. /*                                                                      */
  380. /************************************************************************/
  381. void get_players(void)
  382. {
  383.   UBYTE i,j,c       ; /* player array database pointers */
  384.  
  385.   /* see how many players they want */
  386.   msg(RED,"Welcome to GRAVITY WARS!  How many players (Press 2-7)") ;
  387.  
  388.   /* only accept ascii '2' - '7' */
  389.   do
  390.   {
  391.     c=getch() ;
  392.     if (!c) getch() ;
  393.   }
  394.   while ((c<'2') || (c>'7')) ;
  395.  
  396.   /* for all the requested players... */
  397.   for (i=0;i<(c-'0');i++)
  398.   {
  399.     create_player(i) ;
  400.   }
  401.  
  402. }     /* function get_players */
  403.  
  404.  
  405. /************************************************************************/
  406. /*                                                                      */
  407. /* This routine figgers out where to put a player.                      */
  408. /* We guess a random place for the player then make sure they are not   */
  409. /* on top of a planet or other player.                                  */
  410. /*                                                                      */
  411. /*  Inputs : None                                                       */
  412. /*                                                                      */
  413. /*    I/Os : None                                                       */
  414. /*                                                                      */
  415. /* Globals : players - player database array                            */
  416. /*           planets - planetary database array                         */
  417. /*                                                                      */
  418. /* Returns : Nothing                                                    */
  419. /*                                                                      */
  420. /************************************************************************/
  421. void create_player(UBYTE i)
  422. {
  423.   UBYTE j           ; /* player & planet pointer */
  424.   UBYTE conflict    ; /* conflict flag */
  425.   UWORD lorgx,lorgy ; /* temporary coordinates */
  426.  
  427.   /* do this until we don't have a conflict */
  428.   do
  429.   {
  430.     /* guess at some coordinates */
  431.     lorgx = 25+random(maxx-50) ;
  432.     lorgy = 25+random(maxy-50) ;
  433.  
  434.     /* start out with no conflicts */
  435.     conflict = FALSE ;
  436.  
  437.     /* see if ship is inside any of the planets */
  438.     for(j=0;j<MAX_PLANETS;j++)
  439.     {
  440.       if (dist(lorgx,lorgy,planets[j].orgx,planets[j].orgy) < (planets[j].radius+20))
  441.       {
  442.         conflict = TRUE ;
  443.       }
  444.     }
  445.  
  446.     /* make sure each ship is at least 100 pixels away from all others */
  447.     if (i!=0) /* don't check first one! */
  448.     {
  449.       for(j=0;j<i;j++)
  450.       {
  451.         if (dist(lorgx,lorgy,players[j].orgx,players[j].orgy) < 100)
  452.         {
  453.           conflict = TRUE ;
  454.         } /* if too close */
  455.       }   /* for exising players */
  456.     }     /* if not 1st */
  457.   }
  458.   while (conflict) ;
  459.  
  460.   /* initialize the array database */
  461.   players[i].speed = 0.0 ;
  462.   players[i].angle = 0.0 ;
  463.   players[i].alive = TRUE ;
  464.   players[i].orgx  = lorgx ;
  465.   players[i].orgy  = lorgy ;
  466.   players[i].outs  = 0 ;
  467.  
  468. } /* function create_player */
  469.  
  470.  
  471. /************************************************************************/
  472. /*                                                                      */
  473. /* This routine draws an "Enterprize-like" looking ship.                */
  474. /* Nothign too sophisticated, just a bunch of brute-force drawing       */
  475. /* commands.  We select the color and draw the ship.  The ship is drawn */
  476. /* centered around the x,y origin                                       */
  477. /*                                                                      */
  478. /*  Inputs : color - color to draw the ship                             */
  479. /*           x,y   - where to put the ship                              */
  480. /*                                                                      */
  481. /*    I/Os : None                                                       */
  482. /*                                                                      */
  483. /* Globals : None                                                       */
  484. /*                                                                      */
  485. /* Returns : Nothing                                                    */
  486. /*                                                                      */
  487. /************************************************************************/
  488. void draw_ship (UWORD color,UWORD x,UWORD y)
  489. {
  490.   setcolor(color) ;
  491.   moveto (x,y) ;
  492.  
  493.   moverel(6,-5) ; /* the saucer */
  494.   linerel(1,0)  ;
  495.   linerel(0,3)  ;
  496.   linerel(-1,0) ;
  497.   moverel(-5,-2);
  498.   linerel(11,0) ;
  499.   linerel(0,1)  ;
  500.   linerel(-11,0);
  501.  
  502.   moverel(1,1)  ; /* bit connecting saucer to bottom part */
  503.   linerel(-1,3) ;
  504.   linerel(1,0)  ;
  505.   linerel(1,-3) ;
  506.   linerel(1,0)  ;
  507.   linerel(-1,3) ;
  508.  
  509.   moverel(-9,1) ; /* bottom part */
  510.   linerel(10,0) ;
  511.   linerel(0,1)  ;
  512.   linerel(-13,0);
  513.   linerel(0,1)  ;
  514.   linerel(13,0) ;
  515.  
  516.   moverel(-9,-3); /* bit connecting bottom part to engines */
  517.   linerel(-1,-3);
  518.   linerel(-1,0) ;
  519.   linerel(1,3)  ;
  520.  
  521.   moverel(-6,-5); /* engines */
  522.   linerel(9,0)  ;
  523.   linerel(0,1)  ;
  524.   linerel(-8,0) ;
  525.  
  526. }
  527.  
  528. /************************************************************************/
  529. /*                                                                      */
  530. /* This routine prompts each (alive) player for new trajectories.       */
  531. /* We go through the entire list of players, asking each one that is    */
  532. /* still alive for a new projectile trajectory. After this questioning  */
  533. /* we see if they want to proceed, re-enter data, or quit.              */
  534. /*                                                                      */
  535. /*  Inputs : None                                                       */
  536. /*                                                                      */
  537. /*    I/Os : None                                                       */
  538. /*                                                                      */
  539. /* Globals : players - player database array                            */
  540. /*                                                                      */
  541. /* Returns : true if they want to quit                                  */
  542. /*                                                                      */
  543. /************************************************************************/
  544. UBYTE get_inputs (void)
  545. {
  546.   UBYTE i,c ;
  547.   char line[80] ;
  548.  
  549.  
  550.   /* until entries OK */
  551.   do
  552.   {
  553.     /* for all players */
  554.     for (i=0;i<MAX_PLAYERS;i++)
  555.     {
  556.       /* only if the ship is still alive do we ask */
  557.       if (players[i].alive)
  558.       {
  559.         /* show them where to shoot */
  560.         draw_compass(players[i].orgx,players[i].orgy,WHITE) ;
  561.  
  562.         do /* until they get it right */
  563.         {
  564.           /* request and get the new angle */
  565.           sprintf (line,"Player %d, Enter angle (0-360, prev %9.4f) >",i+1,players[i].angle) ;
  566.           msg(i+9,line) ;
  567.           get_num(385,&(players[i].angle)) ;
  568.         }
  569.         while ((players[i].angle<0.0) || (players[i].angle>360.0)) ;
  570.  
  571.         do /* until they get it right */
  572.         {
  573.           /* request and get the new speed */
  574.           sprintf (line,"Player %d, Enter speed (0-40, prev %9.4f) >",i+1,players[i].speed) ;
  575.           msg(i+9,line) ;
  576.           get_num(385,&(players[i].speed)) ;
  577.         }
  578.         while ((players[i].speed<0.0) || (players[i].speed>40.0)) ;
  579.  
  580.         /* erase the compas */
  581.         draw_compass(players[i].orgx,players[i].orgy,BLACK) ;
  582.       } /* if player is alive */
  583.     }   /* for all potential players */
  584.  
  585.     /* see what they want to do now */
  586.     msg (RED,"<G>o, <R>e-enter data, <C>hange Players, or <Q>uit early") ;
  587.     do
  588.     {
  589.       c=getch() ;
  590.       if (!c) getch() ;
  591.       c=tolower(c) ;
  592.     }
  593.     while ((c!='g')&&(c!='r')&&(c!='q')&&(c!='c')) ;
  594.  
  595.   /* if they request player change, go do it */
  596.   if (c=='c')
  597.   {
  598.     change_players() ;
  599.     /* after a change, repeat entries */
  600.     c='r' ;
  601.   }
  602.  
  603.   } /* if repeat, go over numbers again */
  604.   while (c=='r') ;
  605.  
  606.   /* return quit status */
  607.   return (c=='q') ;
  608. } /* function get_inputs */
  609.  
  610. /************************************************************************/
  611. /*                                                                      */
  612. /* This routine gets a floating point number from the user.             */
  613. /* we are told where to print this number as we get it and inherit      */
  614. /* whatever color is currently being used.  Each time a key is pressed  */
  615. /* we erase whatever was on the line before, add the character just     */
  616. /* typed and print it.  Only digits, periods, enter and backspace are   */
  617. /* allowed.  If the user presses enter without entering anything, we    */
  618. /* do not alter the previous value.
  619. /*                                                                      */
  620. /*  Inputs : x - where in the entry line to "get" the number            */
  621. /*                                                                      */
  622. /*    I/Os : *val - the old (and new) number                            */
  623. /*                                                                      */
  624. /* Globals : None                                                       */
  625. /*                                                                      */
  626. /* Returns : Nothing                                                    */
  627. /*                                                                      */
  628. /************************************************************************/
  629. void get_num(UWORD x,float *val)
  630. {
  631.   char line[20] ; /* the text number     */
  632.   UBYTE i,c     ; /* pointer & character */
  633.   float num     ; /* new number          */
  634.  
  635.   do /* until we get a valid number */
  636.   {
  637.  
  638.     /* initialize the line */
  639.     line[0]='\0' ;
  640.     i=0 ;
  641.  
  642.     do /* until enter is pressed */
  643.     {
  644.       /* erase the old line & show current line characters */
  645.       setfillstyle (SOLID_FILL,BLACK) ;
  646.       bar(x,maxy+1,x+200,maxy+21) ;
  647.       outtextxy(x,maxy,line) ;
  648.  
  649.       /* get a key, if extended, leave as 0 */
  650.       c=getch()  ;
  651.       if (!c) getch() ;
  652.  
  653.       /* if backspace and backspacable, erase current char */
  654.       if ((c=='\b') && (i>0))
  655.       {
  656.         i-- ;
  657.         line[i] = '\0' ;
  658.       }
  659.  
  660.       /* if digit or decimal, add to line */
  661.       if ((isdigit(c) || (c=='.')) && (i<19))
  662.       {
  663.         line[i] = c ;
  664.         i++ ;
  665.         line[i] = '\0' ;
  666.       }
  667.  
  668.     }
  669.     while (c!=13) ;
  670.   }
  671.   /* if CR, then must be blank line or valid number */
  672.   while ((line[0]!='\0') && (sscanf(line,"%f",&num)!=1)) ;
  673.  
  674.   /* if not blank line, assign new number */
  675.   if (line[0]!='\0')
  676.   {
  677.     *val = num ;
  678.   }
  679.  
  680. } /* function get_num */
  681.  
  682.  
  683. /************************************************************************/
  684. /*                                                                      */
  685. /* This routine
  686. /*                                                                      */
  687. /*  Inputs : None                                                       */
  688. /*                                                                      */
  689. /*    I/Os : None                                                       */
  690. /*                                                                      */
  691. /* Globals : players - global player database array                     */
  692. /*                                                                      */
  693. /* Returns : Nothing                                                    */
  694. /*                                                                      */
  695. /************************************************************************/
  696. void change_players(void)
  697. {
  698.  
  699.   UBYTE playing[MAX_PLAYERS] ;
  700.   UBYTE i,c                  ;
  701.   UBYTE mesg[100]            ;
  702.  
  703.   /* creat a local array with players in & out */
  704.   for (i=0;i<MAX_PLAYERS;i++)
  705.   {
  706.     playing[i]=((players[i].alive)||(!players[i].alive&&(players[i].outs>0))) ;
  707.   }
  708.  
  709.   do /* until they press enter */
  710.   {
  711.     /* the basic message */
  712.     strcpy(mesg,"Players: [1234567] Press # to add/delete, Enter to end") ;
  713.  
  714.     /* erase the #'s for the players not playing */
  715.     for (i=0;i<MAX_PLAYERS;i++)
  716.     {
  717.       if (!playing[i])
  718.       {
  719.         mesg[10+i]=' ' ;
  720.       }
  721.     }
  722.  
  723.     /* write out the status message */
  724.     msg(RED,mesg) ;
  725.  
  726.     /* get a char, ignore "special" chars */
  727.     c=getch() ;
  728.     if (!c) getch() ;
  729.  
  730.     /* if 1-7, invert playing status */
  731.     if ((c>'0')&&(c<'8'))
  732.     {
  733.        playing[c-'1']=!playing[c-'1'] ;
  734.     }
  735.  
  736.   }
  737.   while (c!=13) ;
  738.  
  739.  
  740.   for (i=0;i<MAX_PLAYERS;i++)
  741.   {
  742.     /* if we need to create a player... */
  743.     if (playing[i]&&(!players[i].alive&&(players[i].outs==0)))
  744.     {
  745.       create_player(i) ;
  746.       draw_ship(i+9,players[i].orgx,players[i].orgy) ;
  747.     } /* create_player /*
  748.  
  749.     /* if we are killing a player... */
  750.     if (!playing[i]&&(players[i].alive||(!players[i].alive&&(players[i].outs>0))))
  751.     {
  752.       players[i].alive=FALSE ;
  753.       players[i].outs = 0    ;
  754.     } /* kill player */
  755.   }  /* for all possible players */
  756.  
  757. } /* function change_players */
  758.  
  759.  
  760. /************************************************************************/
  761. /*                                                                      */
  762. /* This routine shows the torpedo path.                                 */
  763. /* In the interests of speed, this is one routine with no subroutines.  */
  764. /* We first set all the torpedos to hit, meaning they are dormant. Then */
  765. /* for each player that is still alive we assign the starting values    */
  766. /* for their torpedo.  These include the starting position and starting */
  767. /* x and y speeds. Finally we set hit to false, signifying a "live"     */
  768. /* torpedo.  Then for each dot for each torpedo we must calculate the   */
  769. /* cumulative gravity by all the planets.  The exact method of doing    */
  770. /* this is explained quite well in the code.  The total accelleration   */
  771. /* then applies a delta to the current x and y velocities which in turn */
  772. /* apply delta values to the x and y coordinates.  We plot the new point*/
  773. /* and then see if we hit anything.  Three things can be hit, the       */
  774. /* edges of the universe, a planet, or another ship.  The first two just*/
  775. /* render the torpedo dead.  The latter clearly kills an opponent (or   */
  776. /* the originator!).  This entire incremental process continues until   */
  777. /* there are no more live torpedos.                                     */
  778. /*                                                                      */
  779. /*  Inputs : None                                                       */
  780. /*                                                                      */
  781. /*    I/Os : None                                                       */
  782. /*                                                                      */
  783. /* Globals : players - player database array                            */
  784. /*           planets - planetary database array                         */
  785. /*                                                                      */
  786. /* Returns : Nothing                                                    */
  787. /*                                                                      */
  788. /************************************************************************/
  789. void show_paths (void)
  790. {
  791.   TORPEDO torps[MAX_PLAYERS] ; /* torpedo data              */
  792.   UWORD i,j,cnt              ; /* general array indexes     */
  793.   float dlx,dly              ;
  794.   float dvx,dvy              ; /* delta V for x and y       */
  795.   float dist,distsq          ; /* planetery distance & ^2   */
  796.  
  797.   /* announce the race */
  798.   msg (RED,"And they're off...") ;
  799.  
  800.   /* select the starting point for the projectiles */
  801.   for (i=0;i<MAX_PLAYERS;i++)
  802.   {
  803.     /* assume no torpedo unless player is still alive */
  804.     torps[i].hit = TRUE ;
  805.     if (players[i].alive)
  806.     {
  807.       /* if player is alive, set up default data for torpedo */
  808.       torps[i].x   =  players[i].orgx + 16*cos(players[i].angle*DEG2RAD) ;
  809.       torps[i].y   =  players[i].orgy - 16*sin(players[i].angle*DEG2RAD) ;
  810.       torps[i].vx  =  players[i].speed * cos(players[i].angle*DEG2RAD) ;
  811.       torps[i].vy  = -players[i].speed * sin(players[i].angle*DEG2RAD) ;
  812.       torps[i].hit = FALSE ;
  813.       putpixel (torps[i].x,torps[i].y,WHITE) ;
  814.     } /* for all planets  */
  815.   }   /* for all torpedos */
  816.  
  817.   do /* until all torpedos hit something */
  818.   {
  819.     /* for each possible torpedo... */
  820.     for (i=0;i<MAX_PLAYERS;i++)
  821.     {
  822.       /* if it hasn't hit anything yet... */
  823.       if (!torps[i].hit)
  824.       {
  825.         /* zero cumulative dv's (accellerations) */
  826.         dvx=0.0 ;
  827.         dvy=0.0 ;
  828.  
  829.         /* calculate accellaration for each planet */
  830.         for (j=0;j<MAX_PLANETS;j++)
  831.         {
  832.  
  833.           /* compute x and y distances */
  834.           dlx  = planets[j].orgx-torps[i].x ;
  835.           dly  = planets[j].orgy-torps[i].y ;
  836.  
  837.           /* compute absolute distance squared & distance */
  838.           distsq = dlx*dlx+dly*dly ;
  839.           dist   = sqrt(distsq) ;
  840.  
  841.           /* absolute acelleration is mass/distance**2            */
  842.           /* vectored acelleration is absolute * cos or sin       */
  843.           /* cos is dx/r, sin is dy/r, know dx,dy, & r so forget  */
  844.           /* computationally expensive cos & sine!                */
  845.           dvx += (planets[j].mass/distsq)*dlx/dist ;
  846.           dvy += (planets[j].mass/distsq)*dly/dist ;
  847.         } /* for all planets */
  848.  
  849.         /* adjust velocities by acelleration */
  850.         torps[i].vx += dvx ;
  851.         torps[i].vy += dvy ;
  852.  
  853.         /* adjust distances by velocity */
  854.         torps[i].x += torps[i].vx/5.0 ;
  855.         torps[i].y += torps[i].vy/5.0 ;
  856.  
  857.         /* draw the dot trail */
  858.         if ((torps[i].x < maxx) && (torps[i].x > 1) &&
  859.             (torps[i].y < maxy) && (torps[i].y > 1))
  860.         {
  861.           putpixel (torps[i].x,torps[i].y,WHITE) ;
  862.         }
  863.  
  864.         /* see if we hit anything */
  865.         check_bounds (&torps[i],i) ;
  866.         check_planets(&torps[i],i) ;
  867.         check_ships  (&torps[i],i) ;
  868.       } /* if torpedo still going */
  869.     }   /* for all torpedos       */
  870.  
  871.     /* count how many torpedos are still going */
  872.     cnt = 0 ;
  873.     for (i=0;i<MAX_PLAYERS;i++)
  874.     {
  875.       if (!torps[i].hit)
  876.       {
  877.         cnt++ ;
  878.       }
  879.     } /* for all torpedos */
  880.  
  881.     delay(20) ;
  882.  
  883.   } /* while there are still torpedos left */
  884.   while ((cnt >0) && !kbhit()) ;
  885. } /* function show_paths */
  886.  
  887. /************************************************************************/
  888. /*                                                                      */
  889. /* This routine figgers out how many players are still alive.           */
  890. /* Before doing that, however, we first see if any players or planets   */
  891. /* are due for re-encarnation.  After raising any dead, we simply count */
  892. /* the number of players that have the alive flag set.                  */
  893. /*                                                                      */
  894. /*  Inputs : None                                                       */
  895. /*                                                                      */
  896. /*    I/Os : None                                                       */
  897. /*                                                                      */
  898. /* Globals : players - player database array                            */
  899. /*                                                                      */
  900. /* Returns : Number of alive players                                    */
  901. /*                                                                      */
  902. /************************************************************************/
  903. UBYTE players_left(void)
  904. {
  905.   UBYTE i,tot=0 ; /* counter & total */
  906.  
  907.   /* check all the planets */
  908.   for (i=0;i<MAX_PLANETS;i++)
  909.   {
  910.     /* if it is currently dead */
  911.     if (planets[i].hits==MAX_PLANET_HITS)
  912.     {
  913.       /* decrement the dead-time count */
  914.       planets[i].outs-- ;
  915.       /* if count expired, re-encarnate the planet */
  916.       if (planets[i].outs==0)
  917.       {
  918.         create_planet(i) ;
  919.         draw_planet(i) ;
  920.       } /* if planet re-encarneted */
  921.     }   /* if a dead planet        */
  922.   }     /* for all planets         */
  923.  
  924.   /* check to see if any players are due for re-encarnation */
  925.   for (i=0;i<MAX_PLAYERS;i++)
  926.   {
  927.     /* if the player is temporarily dead... */
  928.     if (!players[i].alive && (players[i].outs>0))
  929.     {
  930.       /* decrement dead-time count */
  931.       players[i].outs-- ;
  932.  
  933.       /* if count expired, re-encarnate player */
  934.       if (players[i].outs==0)
  935.       {
  936.         create_player(i) ;
  937.         draw_ship(i+9,players[i].orgx,players[i].orgy) ;
  938.       } /* if player re-encarneted */
  939.     }   /* if player dead          */
  940.   }     /* for all players         */
  941.  
  942.   /* for all possible players */
  943.   for (i=0;i<MAX_PLAYERS;i++)
  944.   {
  945.     /* if alive, increment total */
  946.     if (players[i].alive)
  947.     {
  948.       tot++ ;
  949.     }
  950.   }
  951.  
  952.   /* return total */
  953.   return (tot) ;
  954. } /* function players_left */
  955.  
  956. /************************************************************************/
  957. /*                                                                      */
  958. /* This routine paints the screen with all the planets and ships.       */
  959. /* We simply traverse the planetary and player arrays drawing the parts.*/
  960. /* We take special care not to draw planets and ships that don't exist. */
  961. /*                                                                      */
  962. /*  Inputs : None                                                       */
  963. /*                                                                      */
  964. /*    I/Os : None                                                       */
  965. /*                                                                      */
  966. /* Globals : players - player database array                            */
  967. /*           planets - planetary database array                         */
  968. /*                                                                      */
  969. /* Returns : Nothing                                                    */
  970. /*                                                                      */
  971. /************************************************************************/
  972. void paint_screen(void)
  973. {
  974.   UBYTE i     ; /* loop counter   */
  975.  
  976.   /* first erase the old screen by painting it all black */
  977.   setfillstyle (SOLID_FILL,BLACK) ;
  978.   bar(1,1,maxx-1,maxy-1) ;
  979.  
  980.   /* for each potential planet */
  981.   for (i=0;i<MAX_PLANETS;i++)
  982.   {
  983.     if (planets[i].hits <MAX_PLANET_HITS)
  984.     {
  985.       draw_planet(i) ;
  986.  
  987.     } /* if the planet exists */
  988.   }   /* for each planet */
  989.  
  990.   /* for each potential player... */
  991.   for (i=0;i<MAX_PLAYERS;i++)
  992.   {
  993.     /* only draw them if they're still alive */
  994.     if (players[i].alive)
  995.     {
  996.       draw_ship(i+9,players[i].orgx,players[i].orgy) ;
  997.     } /* if alive              */
  998.   }   /* all players           */
  999. }     /* function paint_screen */
  1000.  
  1001.  
  1002. /************************************************************************/
  1003. /*                                                                      */
  1004. /* This routine draws a planet.                                         */
  1005. /*                                                                      */
  1006. /*  Inputs : None                                                       */
  1007. /*                                                                      */
  1008. /*    I/Os : None                                                       */
  1009. /*                                                                      */
  1010. /* Globals : planets - planetary database array                         */
  1011. /*                                                                      */
  1012. /* Returns : Nothing                                                    */
  1013. /*                                                                      */
  1014. /************************************************************************/
  1015. void draw_planet(UBYTE i)
  1016. {
  1017.  
  1018.   UWORD color ; /*planetary color */
  1019.  
  1020.   /* set the color (border & fill), & fill a planetary circle */
  1021.   switch (planets[i].density)
  1022.   {
  1023.     case 1 : color = RED   ; break ;
  1024.     case 2 : color = GREEN ; break ;
  1025.     case 3 : color = BLUE  ; break ;
  1026.     case 4 : color = WHITE ; break ;
  1027.   }
  1028.  
  1029.   setcolor(color) ;
  1030.   setfillstyle (SOLID_FILL,color) ;
  1031.   fillellipse(planets[i].orgx,planets[i].orgy,planets[i].radius,planets[i].radius);
  1032.  
  1033.   if (bnw)
  1034.   {
  1035.     setcolor(BLACK) ;
  1036.     switch (planets[i].density)
  1037.     {
  1038.       case 1 : outtextxy(planets[i].orgx-3,planets[i].orgy-7,"L") ; break ;
  1039.       case 2 : outtextxy(planets[i].orgx-3,planets[i].orgy-7,"M") ; break ;
  1040.       case 3 : outtextxy(planets[i].orgx-3,planets[i].orgy-7,"H") ; break ;
  1041.       case 4 :                                                    ; break ;
  1042.     }
  1043.   }
  1044.  
  1045.  
  1046. } /* function draw_planet */
  1047.  
  1048.  
  1049. /************************************************************************/
  1050. /*                                                                      */
  1051. /* This routine checks to see if the projectile has gone too far.       */
  1052. /* The actual playing field is the equivilent of nine pyysical screens. */
  1053. /* The dimesions range from -max_ to 2*max_.  If we are outside this    */
  1054. /* range, we declare a "hit", calling the torpedo dead.                 */
  1055. /*                                                                      */
  1056. /*  Inputs : None                                                       */
  1057. /*                                                                      */
  1058. /*    I/Os : torp - torpedo data                                        */
  1059. /*                                                                      */
  1060. /* Globals : maxx,maxy                                                  */
  1061. /*                                                                      */
  1062. /* Returns : Nothing                                                    */
  1063. /*                                                                      */
  1064. /************************************************************************/
  1065. void check_bounds (TORPEDO *torp, UBYTE src)
  1066. {
  1067.   char line[80] ;
  1068.  
  1069.   /* check limits */
  1070.   if ((torp->x > maxx*2) || (torp->x < -maxx) ||
  1071.       (torp->y > maxy*2) || (torp->y < -maxy))
  1072.   {
  1073.     sprintf (line,"Player %d's torpedo left the field.",src+1) ;
  1074.     msg(src+9,line) ;
  1075.     torp->hit = TRUE ;
  1076.   }
  1077.  
  1078. }
  1079.  
  1080. /************************************************************************/
  1081. /*                                                                      */
  1082. /* This routine checks to see if we hit a planet.                       */
  1083. /* This is a two-step process.  For each planet we first check to see   */
  1084. /* if we are within a box surrounding the planet.  If we are then we do */
  1085. /* the computationally expensive distance calculation  This process     */
  1086. /* save compute time while showing trajectories.                        */
  1087. /* If a missile does hit a planet we mark the event with no fanfare.    */
  1088. /* That is, unless we have hit the planet too many times.  If we reach  */
  1089. /* this #defined limit, the planet blows up.  The instant it blows up   */
  1090. /* it no longer has any gravitational pull.  This usually messes up     */
  1091. /* any shots all the other players were working on.
  1092. /*                                                                      */
  1093. /*  Inputs : None                                                       */
  1094. /*                                                                      */
  1095. /*    I/Os : torp - torpedo data                                        */
  1096. /*                                                                      */
  1097. /* Globals : planets - planetary database array                         */
  1098. /*                                                                      */
  1099. /* Returns : Nothing                                                    */
  1100. /*                                                                      */
  1101. /************************************************************************/
  1102. void check_planets (TORPEDO *torp, UBYTE src)
  1103. {
  1104.   UBYTE i,j ;
  1105.   WORD lx,ly ;
  1106.   char line[80] ;
  1107.  
  1108.   lx=torp->x ;
  1109.   ly=torp->y ;
  1110.  
  1111.   for (i=0;i<MAX_PLANETS;i++)
  1112.   {
  1113.     if ((planets[i].hits <MAX_PLANET_HITS) &&
  1114.         (abs(lx-planets[i].orgx) < planets[i].radius) &&
  1115.         (abs(ly-planets[i].orgy) < planets[i].radius))
  1116.     {
  1117.       if (dist(lx,ly,planets[i].orgx,planets[i].orgy) <= planets[i].radius)
  1118.       {
  1119.         sprintf (line,"Player %d's torpedo hit a planet.",src+1) ;
  1120.         msg(src+9,line) ;
  1121.         torp->hit = TRUE ;
  1122.         planets[i].hits++ ;
  1123.         if (planets[i].hits >= MAX_PLANET_HITS)
  1124.         {
  1125.           planets[i].outs = PLANET_OUTS+1 ;
  1126.           planets[i].mass = 0.0 ;
  1127.  
  1128.           /* now explode the planet */
  1129.           setcolor(BLACK) ;
  1130.           setfillstyle (SOLID_FILL,BLACK) ;
  1131.  
  1132.           /* set of expanding filled red circles */
  1133.           for (j=2;j<planets[i].radius+1;j++)
  1134.           {
  1135.             fillellipse(planets[i].orgx,planets[i].orgy,j,j) ;
  1136.             delay (20) ;
  1137.           } /* planetary explosion loop         */
  1138.         }   /* if we hit a planet once too many */
  1139.       }     /* if really a hit                  */
  1140.     }       /* if torpedo inside planetary box  */
  1141.   }         /* for all planets                  */
  1142. }           /* function check planets           */
  1143.  
  1144.  
  1145. /************************************************************************/
  1146. /*                                                                      */
  1147. /* This routine checks to see if we hit another (or our own) ship.      */
  1148. /* We check all ships, including the original one.  The ship is defined */
  1149. /* to be a boxed area 12 pixels either side of the origin and 5 pixels  */
  1150. /* up and down.  If we do hit the ship we show a sortof explosion.      */
  1151. /*                                                                      */
  1152. /*  Inputs : None                                                       */
  1153. /*                                                                      */
  1154. /*    I/Os : torp - torpedo data                                        */
  1155. /*                                                                      */
  1156. /* Globals : players - player database array                            */
  1157. /*                                                                      */
  1158. /* Returns : Nothing                                                    */
  1159. /*                                                                      */
  1160. /************************************************************************/
  1161. void check_ships (TORPEDO *torp, UBYTE src)
  1162. {
  1163.   UBYTE i ;
  1164.   WORD lx,ly ;
  1165.   char line[80] ;
  1166.  
  1167.   lx=torp->x ;
  1168.   ly=torp->y ;
  1169.  
  1170.   for (i=0;i<MAX_PLAYERS;i++)
  1171.   {
  1172.     if (players[i].alive)
  1173.     {
  1174.       if ((abs(lx-players[i].orgx) <= 12) &&
  1175.           (abs(ly-players[i].orgy) <= 5 ))
  1176.       {
  1177.         sprintf (line,"Player %d hit Player %d!",src+1,i+1) ;
  1178.         msg(src+9,line) ;
  1179.         torp->hit = TRUE ;
  1180.         explode(players[i].orgx,players[i].orgy) ;
  1181.         players[i].alive = FALSE ;
  1182.         players[i].outs  = PLAYER_OUTS+1 ;
  1183.       } /* if we hit the ship     */
  1184.     }   /* if ship exists         */
  1185.   }     /* for all possible ships */
  1186. }       /* function check planets */
  1187.  
  1188.  
  1189. /************************************************************************/
  1190. /*                                                                      */
  1191. /* This routine draws a simple explosion.                               */
  1192. /* We do this by simply drawing a series of filled circles starting with*/
  1193. /* a radius of two to 20.  We then repeat the process with "black"      */
  1194. /* circles, erasing the previous spheres.                               */
  1195. /*                                                                      */
  1196. /*  Inputs : x,y the place for the explosion                            */
  1197. /*                                                                      */
  1198. /*    I/Os : None                                                       */
  1199. /*                                                                      */
  1200. /* Globals : None                                                       */
  1201. /*                                                                      */
  1202. /* Returns : Nothing                                                    */
  1203. /*                                                                      */
  1204. /************************************************************************/
  1205. void explode(UWORD x, UWORD y)
  1206. {
  1207.   UWORD i ; /* counter */
  1208.  
  1209.   /* red first */
  1210.   setcolor(RED) ;
  1211.   setfillstyle (SOLID_FILL,RED) ;
  1212.  
  1213.   /* set of expanding filled red circles */
  1214.   for (i=2;i<20;i++)
  1215.   {
  1216.     fillellipse(x,y,i,i) ;
  1217.     delay (20) ;
  1218.   }
  1219.  
  1220.   /* now erase the red sphere */
  1221.   setcolor(BLACK) ;
  1222.   setfillstyle (SOLID_FILL,BLACK) ;
  1223.  
  1224.   /* set of expanding filled red circles */
  1225.   for (i=2;i<20;i++)
  1226.   {
  1227.     fillellipse(x,y,i,i) ;
  1228.     delay (20) ;
  1229.   }
  1230. } /* function explode */
  1231.  
  1232. /************************************************************************/
  1233. /*                                                                      */
  1234. /* This routine draws a compass around a ship                           */
  1235. /* We simply draw a circle around the ship and show the tick marks for  */
  1236. /* the important degree points.  The color can be specified.            */
  1237. /*                                                                      */
  1238. /*  Inputs : x,y   - the place to put the compass                       */
  1239. /*           color - the color to draw the compass                      */
  1240. /*                                                                      */
  1241. /*    I/Os : None                                                       */
  1242. /*                                                                      */
  1243. /* Globals : None                                                       */
  1244. /*                                                                      */
  1245. /* Returns : Nothing                                                    */
  1246. /*                                                                      */
  1247. /************************************************************************/
  1248.  
  1249. void draw_compass(UWORD x, UWORD y, UWORD color)
  1250. {
  1251.   UBYTE i ;
  1252.   float a ;
  1253.  
  1254.   setcolor(color) ;
  1255.   circle(x,y,25) ;
  1256.  
  1257.   for (i=0;i<12;i++)
  1258.   {
  1259.     a=i*30.0*DEG2RAD ;
  1260.     line(x+20*cos(a),y-20*sin(a),x+30*cos(a),y-30*sin(a)) ;
  1261.   }
  1262.  
  1263.   outtextxy(x+35,y-8 ,"0") ;
  1264.   outtextxy(x-7 ,y-45,"90") ;
  1265.   outtextxy(x-55,y-8 ,"180") ;
  1266.   outtextxy(x-10,y+30,"270") ;
  1267.  
  1268. }