home *** CD-ROM | disk | FTP | other *** search
/ Black Art of 3D Game Programming / Black_Art_of_3D_Game_Programming.iso / source / borland / chap_9 / blazem1.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-26  |  54.7 KB  |  2,162 lines

  1.  
  2. // I N C L U D E S ///////////////////////////////////////////////////////////
  3.  
  4. #include <io.h>
  5. #include <conio.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <dos.h>
  9. #include <bios.h>
  10. #include <fcntl.h>
  11. #include <memory.h>
  12. #include <malloc.h>
  13. #include <math.h>
  14. #include <string.h>
  15.  
  16. // include all of our stuff
  17.  
  18. #include "black3.h"
  19. #include "black4.h"
  20. #include "black5.h"
  21. #include "black6.h"
  22. #include "black8.h"
  23. #include "black9.h"
  24.  
  25. #include "blazem0.h"
  26.  
  27. ////////////////////////////////////////////////////////////////////////////////
  28.  
  29. int Get_Line(char *buffer)
  30. {
  31. // this function implements a crude line editor, it's used to input strings
  32. // from keyboard
  33.  
  34. int index=0,
  35.     ch;
  36.  
  37. // get the input string
  38.  
  39. while(1)
  40.      {
  41.  
  42.      // has user hiy a key?
  43.  
  44.      if (kbhit())
  45.         {
  46.         // make a sound
  47.  
  48.         Digital_FX_Play(BLZKEY_VOC,2);
  49.  
  50.         // get the key
  51.  
  52.         ch = getch();
  53.  
  54.         // test for a numeric character
  55.  
  56.         if (ch>='0' && ch<='9')
  57.            {
  58.  
  59.            buffer[index]   = ch;
  60.            buffer[index+1] = 0;
  61.  
  62.            Font_Engine_1(DISPLAY_X+2,DISPLAY_Y+2+8,0,0,
  63.                          buffer,
  64.                          video_buffer);
  65.  
  66.            // test if end of line reached
  67.  
  68.            if (++index==12)
  69.               index=11;
  70.  
  71.            } // end if numeric
  72.         else
  73.         if (ch==13)  // test for enter
  74.            {
  75.            // user is done, so exit
  76.  
  77.            // null terminate
  78.  
  79.            buffer[index] = 0;
  80.  
  81.            return(1);
  82.  
  83.            } // end if carriage return
  84.         else
  85.         if (ch==8 || ch==127) // back space or delete
  86.            {
  87.            if (--index <0)
  88.               index=0;
  89.  
  90.            buffer[index]  =' ';
  91.            buffer[index+1]=0;
  92.  
  93.            Font_Engine_1(DISPLAY_X+2,DISPLAY_Y+2+8,0,0,
  94.                          buffer,
  95.                          video_buffer);
  96.  
  97.            buffer[index]=0;
  98.  
  99.            // erase the character
  100.  
  101.            } // end if backspace
  102.         else
  103.         if (ch==27)
  104.            return(0);
  105.  
  106.         } // end if kbhit
  107.  
  108.      } // end while
  109.  
  110. } // end Get_Line
  111.  
  112. //////////////////////////////////////////////////////////////////////////////
  113.  
  114. void Init_Novas(void)
  115. {
  116. // this function initializes all the super novas
  117.  
  118. int index_n, // looping variables
  119.     index_c;
  120.  
  121. // process each nova
  122.  
  123. for (index_n=0; index_n<NUM_NOVAS; index_n++)
  124.     {
  125.  
  126.     // set all novas to inactive
  127.  
  128.     novas[index_n].state    = NOVA_INACTIVE;
  129.     novas[index_n].lifetime = 0;
  130.  
  131.     // intialize each cinder
  132.  
  133.     for (index_c=0; index_c<NUM_CINDERS; index_c++)
  134.         {
  135.         // clear out all fields
  136.  
  137.         novas[index_n].cinders[index_c].x = 0;
  138.         novas[index_n].cinders[index_c].y = 0;
  139.  
  140.         novas[index_n].cinders[index_c].xv = 0;
  141.         novas[index_n].cinders[index_c].yv = 0;
  142.  
  143.         novas[index_n].cinders[index_c].sx = 0;
  144.         novas[index_n].cinders[index_c].sy = 0;
  145.  
  146.         novas[index_n].cinders[index_c].color = 0;
  147.         novas[index_n].cinders[index_c].back_color = 0;
  148.  
  149.         // set timing fields
  150.  
  151.         novas[index_n].cinders[index_c].lifetime = 0;
  152.  
  153.         novas[index_n].cinders[index_c].counter   = 0;
  154.         novas[index_n].cinders[index_c].threshold = 0;
  155.  
  156.         } // end for index
  157.  
  158.     } // end for index_n
  159.  
  160. } // end Init_Novas
  161.  
  162. /////////////////////////////////////////////////////////////////////////////
  163.  
  164. void Erase_Novas(void)
  165. {
  166. // this function replaces the what was under the novas
  167.  
  168. int index_n, // looping variables
  169.     index_c;
  170.  
  171. particle_ptr bits;  // used as a high speed alias into cinders array
  172.  
  173. // process each nova
  174.  
  175. for (index_n=0; index_n<NUM_NOVAS; index_n++)
  176.     {
  177.  
  178.     // is this nova active ?
  179.  
  180.     if (novas[index_n].state==NOVA_ACTIVE)
  181.        {
  182.  
  183.        // alias a pointer to cinder array for speed
  184.  
  185.        bits = (particle_ptr)novas[index_n].cinders;
  186.  
  187.        // process each cinder
  188.  
  189.        for (index_c=0; index_c<NUM_CINDERS; index_c++)
  190.            {
  191.  
  192.            // is this cinder visible?
  193.  
  194.            if (bits[index_c].visible)
  195.               Write_Pixel_DB(bits[index_c].sx,bits[index_c].sy,
  196.                              bits[index_c].back_color);
  197.  
  198.            } // end for index_c
  199.  
  200.        } // end if nova active
  201.  
  202.     } // end for index_n
  203.  
  204. } // end Erase_Novas
  205.  
  206. /////////////////////////////////////////////////////////////////////////////
  207.  
  208. void Under_Novas(void)
  209. {
  210.  
  211. // this function scans whats under a nova
  212.  
  213. int index_n, // looping variables
  214.     index_c,
  215.     px_window,   // the starting postion of the players window
  216.     py_window,
  217.     cx,cy;       // local aliases for speed
  218.  
  219. particle_ptr bits;  // used as a high speed alias into cinders array
  220.  
  221. // compute starting position of players window so screen mapping can be done
  222.  
  223. px_window = players_x - 160+11;
  224. py_window = players_y - 100+9;
  225.  
  226. // process each nova
  227.  
  228. for (index_n=0; index_n<NUM_NOVAS; index_n++)
  229.     {
  230.  
  231.     // is this nova active ?
  232.  
  233.     if (novas[index_n].state==NOVA_ACTIVE)
  234.        {
  235.  
  236.        // alias a pointer to cinder array for speed
  237.  
  238.        bits = (particle_ptr)novas[index_n].cinders;
  239.  
  240.        // process each cinder
  241.  
  242.        for (index_c=0; index_c<NUM_CINDERS; index_c++)
  243.            {
  244.  
  245.            cx=bits[index_c].sx = (bits[index_c].x - px_window);
  246.            cy=bits[index_c].sy = (bits[index_c].y - py_window);
  247.  
  248.            // test if cinder is visible on screen?
  249.  
  250.            if (cx>=320 || cx <0 || cy>=200  || cy<0)
  251.               {
  252.  
  253.               // this cindere is invisible and has been clipped
  254.  
  255.               bits[index_c].visible = 0;
  256.  
  257.               // process next cinder
  258.  
  259.               continue;
  260.  
  261.               } // end visibility test
  262.  
  263.            // scan under cinder
  264.  
  265.            bits[index_c].back_color = Read_Pixel_DB(cx,cy);
  266.  
  267.            // set visibility flag
  268.  
  269.            bits[index_c].visible = 1;
  270.  
  271.            } // end for index_c
  272.  
  273.        } // end if nova active
  274.  
  275.     } // end for index_n
  276.  
  277. } // end Under_Novas
  278.  
  279. /////////////////////////////////////////////////////////////////////////////
  280.  
  281. void Draw_Novas(void)
  282. {
  283.  
  284. // this function draws the novas
  285.  
  286. int index_n, // looping variables
  287.     index_c;
  288.  
  289. particle_ptr bits;  // used as a high speed alias into cinders array
  290.  
  291. // process each nova
  292.  
  293. for (index_n=0; index_n<NUM_NOVAS; index_n++)
  294.     {
  295.  
  296.     // is this nova active ?
  297.  
  298.     if (novas[index_n].state==NOVA_ACTIVE)
  299.        {
  300.  
  301.        // alias a pointer to cinder array for speed
  302.  
  303.        bits = (particle_ptr)novas[index_n].cinders;
  304.  
  305.        // process each cinder
  306.  
  307.        for (index_c=0; index_c<NUM_CINDERS; index_c++)
  308.            {
  309.  
  310.            // is this cinder visible?
  311.  
  312.            if (bits[index_c].visible)
  313.               Write_Pixel_DB(bits[index_c].sx,bits[index_c].sy,
  314.                              bits[index_c].color);
  315.  
  316.            } // end for index_c
  317.  
  318.        } // end if nova active
  319.  
  320.     } // end for index_n
  321.  
  322. } // end Draw_Novas
  323.  
  324. /////////////////////////////////////////////////////////////////////////////
  325.  
  326. void Move_Novas(void)
  327. {
  328.  
  329. // this function draws the novas
  330.  
  331. int index_n, // looping variables
  332.     index_c;
  333.  
  334. particle_ptr bits;  // used as a high speed alias into cinders array
  335.  
  336. // process each nova
  337.  
  338. for (index_n=0; index_n<NUM_NOVAS; index_n++)
  339.     {
  340.  
  341.     // is this nova active ?
  342.  
  343.     if (novas[index_n].state==NOVA_ACTIVE)
  344.        {
  345.  
  346.        // alias a pointer to cinder array for speed
  347.  
  348.        bits = (particle_ptr)novas[index_n].cinders;
  349.  
  350.        // process each cinder
  351.  
  352.        for (index_c=0; index_c<NUM_CINDERS; index_c++)
  353.            {
  354.            // move the cinder
  355.  
  356.            bits[index_c].x+=bits[index_c].xv;
  357.            bits[index_c].y+=bits[index_c].yv;
  358.  
  359.            // animate the cinder color
  360.  
  361.            if (++bits[index_c].counter > bits[index_c].threshold)
  362.               {
  363.               // decrement color
  364.  
  365.               if (++bits[index_c].color >= CINDER_END_COLOR)
  366.                  bits[index_c].color = CINDER_END_COLOR;
  367.  
  368.               // reset counter
  369.  
  370.               bits[index_c].counter = 0;
  371.  
  372.               } // end if color change
  373.  
  374.            } // end for index_c
  375.  
  376.        // age the nova
  377.  
  378.        if (--novas[index_n].lifetime<=0)
  379.           novas[index_n].state=NOVA_INACTIVE;
  380.  
  381.        } // end if nova active
  382.  
  383.     } // end for index_n
  384.  
  385. } // end Move_Novas
  386.  
  387. ///////////////////////////////////////////////////////////////////////////////
  388.  
  389. void Start_Nova(int x,int y)
  390. {
  391. // this function starts a super nova explosion
  392.  
  393. int index_c, // loopnig variables
  394.     index_n;
  395.  
  396.  
  397. // hunt for an inactive nove
  398.  
  399. for (index_n=0; index_n<NUM_NOVAS; index_n++)
  400.     {
  401.  
  402.     // test for inactive nova
  403.  
  404.     if (novas[index_n].state==NOVA_INACTIVE)
  405.        {
  406.  
  407.        // activate nova
  408.  
  409.        novas[index_n].state = NOVA_ACTIVE;
  410.  
  411.        // set lifetime in frames
  412.  
  413.        novas[index_n].lifetime = 50 + rand()%20;
  414.  
  415.        // intialize each cinder or restart a single cinder
  416.  
  417.        for (index_c=0; index_c<NUM_CINDERS; index_c++)
  418.            {
  419.            // fill in position, velocity and color of cinder
  420.  
  421.            novas[index_n].cinders[index_c].x   = x;
  422.            novas[index_n].cinders[index_c].y   = y;
  423.            novas[index_n].cinders[index_c].sx  = 0;
  424.            novas[index_n].cinders[index_c].sy  = 0;
  425.            novas[index_n].cinders[index_c].xv  = -8 + rand()%16;
  426.            novas[index_n].cinders[index_c].yv  = -8 + rand()%16;
  427.  
  428.            novas[index_n].cinders[index_c].color      = CINDER_START_COLOR;
  429.            novas[index_n].cinders[index_c].back_color = 0;
  430.  
  431.            // set timing fields
  432.  
  433.            novas[index_n].cinders[index_c].counter = 0;
  434.            novas[index_n].cinders[index_c].threshold = 2 + rand()%6;
  435.  
  436.            } // end for index_c
  437.  
  438.            // make sound
  439.  
  440.            Digital_FX_Play(BLZEXP2_VOC,0);
  441.  
  442.        // break out of out for
  443.  
  444.        break;
  445.  
  446.        } // end if nova found
  447.  
  448.     } // end for index_n
  449.  
  450. } // end Start_Nova
  451.  
  452. //////////////////////////////////////////////////////////////////////////////
  453.  
  454. void Line_H_2(int x1,int x2,int y,int color,unsigned char far *dest)
  455. {
  456. // draw a horizontal line to the destination buffer
  457.  
  458. _fmemset((char far *)(dest + ((y<<8) + (y<<6)) + x1),
  459.          (unsigned char)color,x2-x1+1);
  460.  
  461. } // end Line_H_2
  462.  
  463. //////////////////////////////////////////////////////////////////////////////
  464.  
  465. void Line_V_2(int y1,int y2,int x,int color, unsigned char far *dest)
  466. {
  467. // draw a vertical line to destination buffer
  468.  
  469. unsigned char far *start_offset; // starting memory offset of line
  470.  
  471. int index; // loop index
  472.  
  473. // compute starting position
  474.  
  475. start_offset = dest + ((y1<<8) + (y1<<6)) + x;
  476.  
  477. for (index=0; index<=y2-y1; index++)
  478.     {
  479.     // set the pixel
  480.  
  481.     *start_offset = (unsigned char)color;
  482.  
  483.     // move downward to next line
  484.  
  485.     start_offset+=320;
  486.  
  487.     } // end for index
  488.  
  489. } // end Line_V_2
  490.  
  491. //////////////////////////////////////////////////////////////////////////////
  492.  
  493. void Init_Stars(void)
  494. {
  495. // this function initializes all the stars in the star field
  496.  
  497. int index; // looping variable
  498.  
  499. for (index=0; index<NUM_STARS; index++)
  500.     {
  501.  
  502.     // select plane that star will be in
  503.  
  504.     switch(rand()%3)
  505.           {
  506.           case STAR_PLANE_0:
  507.                {
  508.                stars[index].color = STAR_COLOR_0;
  509.                stars[index].plane = STAR_PLANE_0;
  510.                } break;
  511.  
  512.           case STAR_PLANE_1:
  513.                {
  514.                stars[index].color = STAR_COLOR_1;
  515.                stars[index].plane = STAR_PLANE_1;
  516.                } break;
  517.  
  518.           case STAR_PLANE_2:
  519.                {
  520.                stars[index].color = STAR_COLOR_2;
  521.                stars[index].plane = STAR_PLANE_2;
  522.                } break;
  523.  
  524.           default:break;
  525.  
  526.           } // end switch
  527.  
  528.      // set fields that aren't plane specific
  529.  
  530.      stars[index].x = rand()%320;  // change this latter to reflect clipping
  531.      stars[index].y = rand()%200;  // region
  532.      stars[index].back_color = 0;
  533.  
  534.     } // end for index
  535.  
  536. } // end Init_Stars
  537.  
  538. /////////////////////////////////////////////////////////////////////////////
  539.  
  540. void Move_Stars(void)
  541. {
  542. // this function moves the star field, note that the star field is always
  543. // ins screen coordinates, otherwise, we would need thousands of stars to
  544. // fill up the universe instead of 50!
  545.  
  546. int index,   // looping variable
  547.     star_x,  // used as fast aliases to star position
  548.     star_y,
  549.     plane_0_dx,
  550.     plane_0_dy,
  551.     plane_1_dx,
  552.     plane_1_dy,
  553.     plane_2_dx,
  554.     plane_2_dy;
  555.  
  556. // pre-compute plane translations
  557.  
  558. plane_0_dx = players_dx >> 2;
  559. plane_0_dy = players_dy >> 2;
  560.  
  561. plane_1_dx = players_dx >> 1;
  562. plane_1_dy = players_dy >> 1;
  563.  
  564. plane_2_dx = players_dx;
  565. plane_2_dy = players_dy;
  566.  
  567. // move all the stars based on the motion of the player
  568.  
  569. for (index=0; index<NUM_STARS; index++)
  570.     {
  571.     // locally cache star position to speed up calculations
  572.  
  573.     star_x = stars[index].x;
  574.     star_y = stars[index].y;
  575.  
  576.     // test which star field star is in so it is translated with correct
  577.     // perspective
  578.  
  579.     switch(stars[index].plane)
  580.           {
  581.           case STAR_PLANE_0:
  582.                {
  583.                // move the star based on differntial motion of player
  584.                // far plane is divided by 4
  585.  
  586.                star_x+=plane_0_dx;
  587.                star_y+=plane_0_dy;
  588.  
  589.                } break;
  590.  
  591.           case STAR_PLANE_1:
  592.                {
  593.                // move the star based on differntial motion of player
  594.                // middle plane is divided by 2
  595.  
  596.                star_x+=plane_1_dx;
  597.                star_y+=plane_1_dy;
  598.  
  599.                } break;
  600.  
  601.           case STAR_PLANE_2:
  602.                {
  603.                // move the star based on differntial motion of player
  604.                // near plane is divided by 1
  605.  
  606.                star_x+=plane_2_dx;
  607.                star_y+=plane_2_dy;
  608.  
  609.                } break;
  610.  
  611.           } // end switch plane
  612.  
  613.     // test if a star has flown off an edge
  614.  
  615.     if (star_x >= 320)
  616.         star_x = star_x-320;
  617.     else
  618.     if (star_x < 0)
  619.         star_x = 320 + star_x;
  620.  
  621.     if (star_y >= 200)
  622.         star_y = star_y-200;
  623.     else
  624.     if (star_y < 0)
  625.         star_y = 200+star_y;
  626.  
  627.     // reset stars position in structure
  628.  
  629.     stars[index].x = star_x;
  630.     stars[index].y = star_y;
  631.  
  632.     } // end for index
  633.  
  634. } // end Move_Stars
  635.  
  636. /////////////////////////////////////////////////////////////////////////////
  637.  
  638. void Draw_Stars(void)
  639. {
  640. // this function draws all the stars
  641.  
  642. int index; // looping variable
  643.  
  644. for (index=0; index<NUM_STARS; index++)
  645.     Write_Pixel_DB(stars[index].x,stars[index].y,stars[index].color);
  646.  
  647. } // end Draw_Stars
  648.  
  649. /////////////////////////////////////////////////////////////////////////////
  650.  
  651. void Erase_Stars(void)
  652. {
  653. // this function erases all the stars
  654.  
  655. int index; // looping variable
  656.  
  657. for (index=0; index<NUM_STARS; index++)
  658.     Write_Pixel_DB(stars[index].x,stars[index].y,stars[index].back_color);
  659.  
  660. } // end Erase_Stars
  661.  
  662. /////////////////////////////////////////////////////////////////////////////
  663.  
  664. void Under_Stars(void)
  665. {
  666. // this function scans under each star
  667.  
  668. int index; // looping variable
  669.  
  670. for (index=0; index<NUM_STARS; index++)
  671.     stars[index].back_color = Read_Pixel_DB(stars[index].x,stars[index].y);
  672.  
  673. } // end Under_Stars
  674.  
  675. /////////////////////////////////////////////////////////////////////////////
  676.  
  677. void Init_Asteroids(int small, int medium, int large)
  678. {
  679. // this function loads in the imagery for the asteroids, allocates all the
  680. // memory for them, sets up all the fields and starts them at random positions
  681.  
  682. int index,  // looping variables
  683.     frame;
  684.  
  685. static int first_time=1;  // used to track first time this function is called
  686.  
  687. // intialize all the large asteroid sprites
  688.  
  689. for (index=START_LARGE_ASTEROIDS; index<=END_LARGE_ASTEROIDS; index++)
  690.     {
  691.  
  692.     if (first_time)
  693.     Sprite_Init((sprite_ptr)&asteroids[index].rock,
  694.                 0,0,
  695.                 ASTEROID_LARGE_WIDTH,ASTEROID_LARGE_HEIGHT,
  696.                 0,0,0,0,0,0);
  697.  
  698.     // set position,velocity and type fields
  699.  
  700.     asteroids[index].xv         = -8 + rand()%16;
  701.     asteroids[index].yv         = -8 + rand()%16;
  702.     asteroids[index].x          = rand()%UNIVERSE_WIDTH;
  703.     asteroids[index].y          = rand()%UNIVERSE_HEIGHT,
  704.  
  705.     asteroids[index].type       = ASTEROID_LARGE;
  706.     asteroids[index].rock.state = ASTEROID_INACTIVE;
  707.  
  708.     // use this to control rotation rate
  709.  
  710.     asteroids[index].rock.threshold_1 = 1 + rand()%3;
  711.  
  712.     } // end for index
  713.  
  714. // intialize all the medium asteroid sprites
  715.  
  716. for (index=START_MEDIUM_ASTEROIDS; index<=END_MEDIUM_ASTEROIDS; index++)
  717.     {
  718.  
  719.     if (first_time)
  720.     Sprite_Init((sprite_ptr)&asteroids[index].rock,
  721.                 0,0,
  722.                 ASTEROID_MEDIUM_WIDTH,ASTEROID_MEDIUM_HEIGHT,
  723.                 0,0,0,0,0,0);
  724.  
  725.     // set velocity and type fields
  726.  
  727.     asteroids[index].xv         = -6 + rand()%12;
  728.     asteroids[index].yv         = -6 + rand()%12;
  729.     asteroids[index].x          = rand()%UNIVERSE_WIDTH;
  730.     asteroids[index].y          = rand()%UNIVERSE_HEIGHT,
  731.     asteroids[index].type       = ASTEROID_MEDIUM;
  732.     asteroids[index].rock.state = ASTEROID_INACTIVE;
  733.  
  734.     // use this to control rotation rate
  735.  
  736.     asteroids[index].rock.threshold_1 = 1 + rand()%3;
  737.  
  738.     } // end for index
  739.  
  740. // intialize all the small asteroid sprites
  741.  
  742. for (index=START_SMALL_ASTEROIDS; index<=END_SMALL_ASTEROIDS; index++)
  743.     {
  744.  
  745.     if (first_time)
  746.     Sprite_Init((sprite_ptr)&asteroids[index].rock,
  747.                 0,0,
  748.                 ASTEROID_SMALL_WIDTH,ASTEROID_SMALL_HEIGHT,
  749.                 0,0,0,0,0,0);
  750.  
  751.     // set velocity and type fields
  752.  
  753.     asteroids[index].xv         = -4 + rand()%8;
  754.     asteroids[index].yv         = -4 + rand()%8;
  755.     asteroids[index].x          = rand()%UNIVERSE_WIDTH;
  756.     asteroids[index].y          = rand()%UNIVERSE_HEIGHT,
  757.     asteroids[index].type       = ASTEROID_SMALL;
  758.     asteroids[index].rock.state = ASTEROID_INACTIVE;
  759.  
  760.     // use this to control rotation rate
  761.  
  762.     asteroids[index].rock.threshold_1 = 1 + rand()%3;
  763.  
  764.     } // end for index
  765.  
  766. // now load the imagery for the large asteroids
  767.  
  768. if (first_time)
  769. {
  770.  
  771. PCX_Init((pcx_picture_ptr)&image_pcx);
  772. PCX_Load("blazelas.pcx", (pcx_picture_ptr)&image_pcx,1);
  773.  
  774. // extract the bitmaps for the asteroid, there are 8 animation cells
  775.  
  776. for (index=0; index<NUM_ASTEROID_FRAMES; index++)
  777.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,
  778.                    (sprite_ptr)&asteroids[START_LARGE_ASTEROIDS].rock,
  779.                    index,index,0);
  780.  
  781. // now alias pointers of remaining asteroids sprites to same data that
  782. // was allocated for the first large asteroids, this saves a lot of memory!
  783.  
  784. for (index=START_LARGE_ASTEROIDS+1; index<=END_LARGE_ASTEROIDS; index++)
  785.     {
  786.     // alias all the frame image pointer of this asteroid to the frames
  787.     // of the first asteroid, no need to replicate this data in memory!
  788.  
  789.     for (frame=0; frame<NUM_ASTEROID_FRAMES; frame++)
  790.         {
  791.         // the image frames are within the sprite which is called rock which
  792.         // is within the asteroid structure
  793.  
  794.         asteroids[index].rock.frames[frame] =
  795.                 asteroids[START_LARGE_ASTEROIDS].rock.frames[frame];
  796.  
  797.         } // end for frame
  798.  
  799.     // set number of frames field
  800.  
  801.     asteroids[index].rock.num_frames =
  802.             asteroids[START_LARGE_ASTEROIDS].rock.num_frames;
  803.  
  804.     } // end for index
  805.  
  806. // delete the pcx file
  807.  
  808. PCX_Delete((pcx_picture_ptr)&image_pcx);
  809.  
  810.  
  811. // now load the imagery for the medium asteroids
  812.  
  813. PCX_Init((pcx_picture_ptr)&image_pcx);
  814. PCX_Load("blazemas.pcx", (pcx_picture_ptr)&image_pcx,1);
  815.  
  816. // extract the bitmaps for the asteroid, there are 8 animation cells
  817.  
  818. for (index=0; index<NUM_ASTEROID_FRAMES; index++)
  819.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,
  820.                    (sprite_ptr)&asteroids[START_MEDIUM_ASTEROIDS].rock,
  821.                    index,index,0);
  822.  
  823. // now alias pointers of remaining asteroids sprites to same data that
  824. // was allocated for the first large asteroids, this saves a lot of memory!
  825.  
  826. for (index=START_MEDIUM_ASTEROIDS+1; index<=END_MEDIUM_ASTEROIDS; index++)
  827.     {
  828.     // alias all the frame image pointer of this asteroid to the frames
  829.     // of the first asteroid, no need to replicate this data in memory!
  830.  
  831.     for (frame=0; frame<NUM_ASTEROID_FRAMES; frame++)
  832.         {
  833.         // the image frames are within the sprite which is called rock which
  834.         // is within the asteroid structure
  835.  
  836.         asteroids[index].rock.frames[frame] =
  837.                        asteroids[START_MEDIUM_ASTEROIDS].rock.frames[frame];
  838.  
  839.         } // end for frame
  840.  
  841.     // set number of frames field
  842.  
  843.     asteroids[index].rock.num_frames =
  844.             asteroids[START_MEDIUM_ASTEROIDS].rock.num_frames;
  845.  
  846.     } // end for index
  847.  
  848. // delete the pcx file
  849.  
  850. PCX_Delete((pcx_picture_ptr)&image_pcx);
  851.  
  852. // finally load the imagery for the small asteroids
  853.  
  854. PCX_Init((pcx_picture_ptr)&image_pcx);
  855. PCX_Load("blazesas.pcx", (pcx_picture_ptr)&image_pcx,1);
  856.  
  857. // extract the bitmaps for the asteroid, there are 8 animation cells
  858.  
  859. for (index=0; index<NUM_ASTEROID_FRAMES; index++)
  860.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,
  861.                    (sprite_ptr)&asteroids[START_SMALL_ASTEROIDS].rock,
  862.                    index,index,0);
  863.  
  864. // now alias pointers of remaining asteroids sprites to same data that
  865. // was allocated for the first large asteroids, this saves a lot of memory!
  866.  
  867. for (index=START_SMALL_ASTEROIDS+1; index<=END_SMALL_ASTEROIDS; index++)
  868.     {
  869.     // alias all the frame image pointer of this asteroid to the frames
  870.     // of the first asteroid, no need to replicate this data in memory!
  871.  
  872.     for (frame=0; frame<NUM_ASTEROID_FRAMES; frame++)
  873.         {
  874.         // the image frames are within the sprite which is called rock which
  875.         // is within the asteroid structure
  876.  
  877.         asteroids[index].rock.frames[frame] =
  878.                        asteroids[START_SMALL_ASTEROIDS].rock.frames[frame];
  879.  
  880.         } // end for frame
  881.  
  882.     // set number of frames field
  883.  
  884.     asteroids[index].rock.num_frames =
  885.             asteroids[START_SMALL_ASTEROIDS].rock.num_frames;
  886.  
  887.     } // end for index
  888.  
  889. // delete the pcx file
  890.  
  891. PCX_Delete((pcx_picture_ptr)&image_pcx);
  892.  
  893. } // end if first time
  894.  
  895.  
  896.  
  897. // now start up the requested number of asteroids
  898.  
  899. // first the large
  900.  
  901. for (index=0; index<large; index++)
  902.     {
  903.     // look for inactive asteroids to start up
  904.  
  905.     if (asteroids[index+START_LARGE_ASTEROIDS].rock.state == ASTEROID_INACTIVE)
  906.         asteroids[index+START_LARGE_ASTEROIDS].rock.state = ASTEROID_ACTIVE;
  907.  
  908.     } // end for index
  909.  
  910. // now the medium
  911.  
  912. for (index=0; index<medium; index++)
  913.     {
  914.     // look for inactive asteroids to start up
  915.  
  916.     if (asteroids[index+START_MEDIUM_ASTEROIDS].rock.state == ASTEROID_INACTIVE)
  917.         asteroids[index+START_MEDIUM_ASTEROIDS].rock.state = ASTEROID_ACTIVE;
  918.  
  919.     } // end for index
  920.  
  921. // finally the small
  922.  
  923. for (index=0; index<small; index++)
  924.     {
  925.     // look for inactive asteroids to start up
  926.  
  927.     if (asteroids[index+START_SMALL_ASTEROIDS].rock.state == ASTEROID_INACTIVE)
  928.         asteroids[index+START_SMALL_ASTEROIDS].rock.state = ASTEROID_ACTIVE;
  929.  
  930.     } // end for index
  931.  
  932. // test if this was the first tmie the function was called
  933.  
  934. if (first_time)
  935.     first_time=0;
  936.  
  937. // what a pain!
  938.  
  939. } // end Init_Asteroids
  940.  
  941. /////////////////////////////////////////////////////////////////////////////
  942.  
  943. void Start_Asteroid(int x,int y,int type)
  944. {
  945. // this function is used to start up an asteroid at the sent position
  946. // later possible implement velocity?
  947.  
  948. int index;  // looping variable
  949.  
  950. // which kind of asteroid is being requested?
  951.  
  952. switch(type)
  953.       {
  954.       case ASTEROID_LARGE:
  955.            {
  956.            // scan for inactive asteroid
  957.  
  958.            for (index=START_LARGE_ASTEROIDS; index<=END_LARGE_ASTEROIDS; index++)
  959.                {
  960.                // is this asteroid being used?
  961.  
  962.                if (asteroids[index].rock.state==ASTEROID_INACTIVE)
  963.                   {
  964.                   // set fields of asteroid and return
  965.                   asteroids[index].xv         = -8 + rand()%16;
  966.                   asteroids[index].yv         = -8 + rand()%16;
  967.                   asteroids[index].x          = x;
  968.                   asteroids[index].y          = y,
  969.                   asteroids[index].rock.state = ASTEROID_ACTIVE;
  970.                   return;
  971.  
  972.                   } // end if found an asteroid
  973.  
  974.                } // end for index
  975.  
  976.            } break;
  977.  
  978.       case ASTEROID_MEDIUM:
  979.            {
  980.            // scan for inactive asteroid
  981.  
  982.            for (index=START_MEDIUM_ASTEROIDS; index<=END_MEDIUM_ASTEROIDS; index++)
  983.                {
  984.                // is this asteroid being used?
  985.  
  986.                if (asteroids[index].rock.state==ASTEROID_INACTIVE)
  987.                   {
  988.                   // set fields of asteroid and return
  989.                   asteroids[index].xv         = -6 + rand()%12;
  990.                   asteroids[index].yv         = -6 + rand()%12;
  991.                   asteroids[index].x          = x;
  992.                   asteroids[index].y          = y,
  993.                   asteroids[index].rock.state = ASTEROID_ACTIVE;
  994.                   return;
  995.  
  996.                   } // end if found an asteroid
  997.  
  998.                } // end for index
  999.  
  1000.            } break;
  1001.  
  1002.       case ASTEROID_SMALL:
  1003.            {
  1004.            // scan for inactive asteroid
  1005.  
  1006.            for (index=START_SMALL_ASTEROIDS; index<=END_SMALL_ASTEROIDS; index++)
  1007.                {
  1008.                // is this asteroid being used?
  1009.  
  1010.                if (asteroids[index].rock.state==ASTEROID_INACTIVE)
  1011.                   {
  1012.                   // set fields of asteroid and return
  1013.  
  1014.                   asteroids[index].xv         = -4 + rand()%8;
  1015.                   asteroids[index].yv         = -4 + rand()%8;
  1016.                   asteroids[index].x          = x;
  1017.                   asteroids[index].y          = y,
  1018.                   asteroids[index].rock.state = ASTEROID_ACTIVE;
  1019.                   return;
  1020.  
  1021.                   } // end if found an asteroid
  1022.  
  1023.                } // end for index
  1024.  
  1025.            } break;
  1026.  
  1027.       } // end swtich
  1028.  
  1029. } // end Start_Asteroid
  1030.  
  1031. //////////////////////////////////////////////////////////////////////////////
  1032.  
  1033. void Erase_Asteroids(void)
  1034. {
  1035. // this function traverses the asteroid list and erases all asteroids that
  1036. // are active
  1037.  
  1038. int index;
  1039.  
  1040. // erase all asteroids that are within screen window
  1041.  
  1042. for (index=0; index<NUM_ASTEROIDS; index++)
  1043.     {
  1044.     // test if this asteroids is active
  1045.  
  1046.     if (asteroids[index].rock.state==ASTEROID_ACTIVE)
  1047.        Sprite_Erase_Clip((sprite_ptr)&asteroids[index].rock,double_buffer);
  1048.  
  1049.     } // end for index
  1050.  
  1051. } // Erase_Asteroids
  1052.  
  1053. //////////////////////////////////////////////////////////////////////////////
  1054.  
  1055. void Draw_Asteroids(void)
  1056. {
  1057. // this function traverses the asteroid list and draws all asteroids that
  1058. // are active
  1059.  
  1060. int index;
  1061.  
  1062. for (index=0; index<NUM_ASTEROIDS; index++)
  1063.     {
  1064.     // test if this asteroids is active
  1065.  
  1066.     if (asteroids[index].rock.state==ASTEROID_ACTIVE)
  1067.         Sprite_Draw_Clip((sprite_ptr)&asteroids[index].rock,double_buffer,1);
  1068.  
  1069.     } // end for index
  1070.  
  1071. } // Draw_Asteroids
  1072.  
  1073. //////////////////////////////////////////////////////////////////////////////
  1074.  
  1075. void Under_Asteroids(void)
  1076. {
  1077.  
  1078. // this function traverses the asteroid list and scans under all asteroids that
  1079. // are active, note that this function is the only one that computes the screen
  1080. // coordinates of the asteroid sprites, placing the computation in the other
  1081. // functions would be redundant, hence the sprite coordinates at this function
  1082. // are undefined, and after this function they have been remapped to the video
  1083. // screen relative to the players position
  1084.  
  1085.  
  1086. int index,
  1087.     px_window,   // the starting postion of the players window
  1088.     py_window;
  1089.  
  1090. // compute starting position of players window so screen mapping can be done
  1091.  
  1092. px_window = players_x - 160+11;
  1093. py_window = players_y - 100+9;
  1094.  
  1095. // now scan under all asteroids
  1096.  
  1097. for (index=0; index<NUM_ASTEROIDS; index++)
  1098.     {
  1099.     // test if this asteroids is active
  1100.  
  1101.     if (asteroids[index].rock.state==ASTEROID_ACTIVE)
  1102.        {
  1103.  
  1104.        // postion asteroid correctly on view screen, note this is very similar
  1105.        // to what we will do in 3-D when we translate all the objects in the
  1106.        // universe to the viewer position
  1107.  
  1108.        asteroids[index].rock.x = (asteroids[index].x-px_window);
  1109.        asteroids[index].rock.y = (asteroids[index].y-py_window);
  1110.  
  1111.        Sprite_Under_Clip((sprite_ptr)&asteroids[index].rock,double_buffer);
  1112.  
  1113.        } // end if active
  1114.  
  1115.     } // end for index
  1116.  
  1117. } // Under_Asteroids
  1118.  
  1119. //////////////////////////////////////////////////////////////////////////////
  1120.  
  1121. void Move_Asteroids(void)
  1122. {
  1123.  
  1124. // this function traverses the asteroid list and moves and test for collsions
  1125. // note that the sprite positions of the asteroids are not touched only the
  1126. // universe or "world" coordinates are
  1127.  
  1128. int index,         // looping variable
  1129.     ast_x,ast_y;   // used for aliasing
  1130.  
  1131. // process each asteroid
  1132.  
  1133. for (index=0; index<NUM_ASTEROIDS; index++)
  1134.     {
  1135.     // test if this asteroids is active
  1136.  
  1137.     if (asteroids[index].rock.state==ASTEROID_ACTIVE)
  1138.        {
  1139.        // move the asteroid
  1140.  
  1141.        ast_x=asteroids[index].x;
  1142.        ast_y=asteroids[index].y;
  1143.  
  1144.        ast_x+=asteroids[index].xv;
  1145.        ast_y+=asteroids[index].yv;
  1146.  
  1147.        // test if asteroid is off screen bounadary
  1148.  
  1149.        if (ast_x>UNIVERSE_WIDTH+UNIVERSE_BORDER)
  1150.            ast_x = -UNIVERSE_BORDER;
  1151.        else
  1152.        if (ast_x<-UNIVERSE_BORDER)
  1153.            ast_x = UNIVERSE_WIDTH+UNIVERSE_BORDER;
  1154.  
  1155.        if (ast_y>UNIVERSE_HEIGHT+UNIVERSE_BORDER)
  1156.            ast_y = -UNIVERSE_BORDER;
  1157.        else
  1158.        if (ast_y<-UNIVERSE_BORDER)
  1159.            ast_y = UNIVERSE_HEIGHT+UNIVERSE_BORDER;
  1160.  
  1161.        // restore variables in structure
  1162.  
  1163.        asteroids[index].x = ast_x;
  1164.        asteroids[index].y = ast_y;
  1165.  
  1166.        // animate asteroid
  1167.  
  1168.        if (++asteroids[index].rock.counter_1>=asteroids[index].rock.threshold_1)
  1169.           {
  1170.           // reset counter
  1171.  
  1172.           asteroids[index].rock.counter_1 = 0;
  1173.  
  1174.           if (++asteroids[index].rock.curr_frame >= NUM_ASTEROID_FRAMES)
  1175.               asteroids[index].rock.curr_frame = 0;
  1176.  
  1177.           } // end if time to animate
  1178.  
  1179.        // test if asteroid has hit player
  1180.  
  1181.        // test for collision
  1182.  
  1183.        if (players_x+SHIP_WIDTH/2  >= ast_x                             &&
  1184.            players_y+SHIP_HEIGHT/2 >= ast_y                             &&
  1185.            players_x+SHIP_WIDTH/2  <= ast_x+asteroids[index].rock.width &&
  1186.            players_y+SHIP_HEIGHT/2 <= ast_y+asteroids[index].rock.height)
  1187.           {
  1188.           // kill the asteroid and the missile
  1189.  
  1190.           asteroids[index].rock.state = ASTEROID_INACTIVE;
  1191.  
  1192.           // what kind of asteroid did we have?
  1193.  
  1194.           switch(asteroids[index].type)
  1195.                 {
  1196.  
  1197.                 case ASTEROID_LARGE:
  1198.                      {
  1199.  
  1200.                      // start one medium and one small asteroid
  1201.                      //  (if possible)
  1202.  
  1203.                      Start_Asteroid(asteroids[index].x,
  1204.                                     asteroids[index].y,
  1205.                                     ASTEROID_MEDIUM);
  1206.  
  1207.                      Start_Asteroid(asteroids[index].x+ASTEROID_LARGE_WIDTH/2,
  1208.                                     asteroids[index].y+ASTEROID_LARGE_HEIGHT/2,
  1209.                                     ASTEROID_SMALL);
  1210.  
  1211.                      } break;
  1212.  
  1213.                 case ASTEROID_MEDIUM:
  1214.                      {
  1215.  
  1216.                      // start two small asteroids (if possible)
  1217.  
  1218.                      Start_Asteroid(asteroids[index].x,
  1219.                                     asteroids[index].y,
  1220.                                     ASTEROID_SMALL);
  1221.  
  1222.                      Start_Asteroid(asteroids[index].x+ASTEROID_MEDIUM_WIDTH/2,
  1223.                                     asteroids[index].y+ASTEROID_MEDIUM_HEIGHT/2,
  1224.                                     ASTEROID_SMALL);
  1225.  
  1226.                      } break;
  1227.  
  1228.                 case ASTEROID_SMALL:
  1229.                      {
  1230.  
  1231.                      // start a randomly positioned asteroid of any size
  1232.                      // at the worm hole
  1233.  
  1234.                      Start_Asteroid(WORMHOLE_X,WORMHOLE_Y,rand()%3);
  1235.  
  1236.                      } break;
  1237.  
  1238.                 default:break;
  1239.  
  1240.                 } // end switch
  1241.  
  1242.           // were shields up?
  1243.  
  1244.           if (players_shields)
  1245.              {
  1246.              // start explosion
  1247.              Start_Explosion(players_x,players_y,2);
  1248.              } // end if shields up
  1249.           else
  1250.              { // say bye bye!
  1251.  
  1252.              Start_Explosion(players_x,players_y,2);
  1253.              Start_Nova(players_x+SHIP_WIDTH/2,players_y+SHIP_HEIGHT/2);
  1254.  
  1255.              // start players death sequence
  1256.  
  1257.              Start_Players_Death();
  1258.  
  1259.              } // end if else
  1260.  
  1261.           } // end if player has been hit
  1262.  
  1263.        // test if asteroid has hit remote
  1264.  
  1265.        // test for collision
  1266.  
  1267.        if (linked)
  1268.        {
  1269.        if (remotes_x+SHIP_WIDTH/2  >= ast_x                             &&
  1270.            remotes_y+SHIP_HEIGHT/2 >= ast_y                             &&
  1271.            remotes_x+SHIP_WIDTH/2  <= ast_x+asteroids[index].rock.width &&
  1272.            remotes_y+SHIP_HEIGHT/2 <= ast_y+asteroids[index].rock.height)
  1273.           {
  1274.           // kill the asteroid and the missile
  1275.  
  1276.           asteroids[index].rock.state = ASTEROID_INACTIVE;
  1277.  
  1278.           // what kind of asteroid did we have?
  1279.  
  1280.           switch(asteroids[index].type)
  1281.                 {
  1282.  
  1283.                 case ASTEROID_LARGE:
  1284.                      {
  1285.                      // start one medium and one small asteroid
  1286.                      //  (if possible)
  1287.  
  1288.                      Start_Asteroid(asteroids[index].x,
  1289.                                     asteroids[index].y,
  1290.                                     ASTEROID_MEDIUM);
  1291.  
  1292.                      Start_Asteroid(asteroids[index].x+ASTEROID_LARGE_WIDTH/2,
  1293.                                     asteroids[index].y+ASTEROID_LARGE_HEIGHT/2,
  1294.                                     ASTEROID_SMALL);
  1295.  
  1296.                      } break;
  1297.  
  1298.                 case ASTEROID_MEDIUM:
  1299.                      {
  1300.                      // start two small asteroids (if possible)
  1301.  
  1302.                      Start_Asteroid(asteroids[index].x,
  1303.                                     asteroids[index].y,
  1304.                                     ASTEROID_SMALL);
  1305.  
  1306.                      Start_Asteroid(asteroids[index].x+ASTEROID_MEDIUM_WIDTH/2,
  1307.                                     asteroids[index].y+ASTEROID_MEDIUM_HEIGHT/2,
  1308.                                     ASTEROID_SMALL);
  1309.  
  1310.                      } break;
  1311.  
  1312.                 case ASTEROID_SMALL:
  1313.                      {
  1314.  
  1315.                      // start a randomly positioned asteroid of any size
  1316.                      // at the worm hole
  1317.  
  1318.                      Start_Asteroid(WORMHOLE_X,WORMHOLE_Y,rand()%3);
  1319.  
  1320.                      } break;
  1321.  
  1322.                 default:break;
  1323.  
  1324.                 } // end switch
  1325.  
  1326.           // were shields up?
  1327.  
  1328.           if (remotes_shields)
  1329.              {
  1330.              // start explosion
  1331.              Start_Explosion(remotes_x,remotes_y,2);
  1332.              } // end if shields up
  1333.           else
  1334.              { // say bye bye!
  1335.  
  1336.              Start_Explosion(remotes_x,remotes_y,2);
  1337.              Start_Nova(remotes_x+SHIP_WIDTH/2,remotes_y+SHIP_HEIGHT/2);
  1338.  
  1339.              // start remotes death sequence
  1340.  
  1341.              Start_Remotes_Death();
  1342.  
  1343.              } // end if else
  1344.  
  1345.           } // end if remote has been hit
  1346.  
  1347.        } // end if linked
  1348.  
  1349.        } // end if asteroids is active
  1350.  
  1351.     } // end for index
  1352.  
  1353. } // Move_Asteroids
  1354.  
  1355. //////////////////////////////////////////////////////////////////////////////
  1356.  
  1357. void Tech_Print(int x,int y,char *string,unsigned char far *destination)
  1358. {
  1359. // this function is used to print text out like a teletypwriter,it looks
  1360. // cool, trust me!
  1361.  
  1362. int length,  // length of input string
  1363.     index,   // looping variable
  1364.     counter; // used to time process
  1365.  
  1366. char buffer[3];  // a little string used to call font engine with
  1367.  
  1368. // compute length of input string
  1369.  
  1370. length = strlen(string);
  1371.  
  1372. // print the string out a character at a time
  1373.  
  1374. for (index=0; index<length; index++)
  1375.     {
  1376.     // the first character is the actual printable character
  1377.  
  1378.     buffer[0] = string[index];
  1379.  
  1380.     // this is a little cursor kind of thing
  1381.  
  1382.     buffer[1] = '<';
  1383.  
  1384.     // null terminate
  1385.  
  1386.     buffer[2] = 0;
  1387.  
  1388.     // print the string
  1389.  
  1390.     Font_Engine_1(x,y,0,0,buffer,destination);
  1391.  
  1392.     // move to next position
  1393.  
  1394.     x+=(TECH_FONT_WIDTH+1);
  1395.  
  1396.     // wait a bit  1/70th of a second
  1397.  
  1398.     Wait_For_Vertical_Retrace();
  1399.  
  1400.     // clear the cursor
  1401.  
  1402.     } // end for
  1403.  
  1404.     // clear the cursor
  1405.  
  1406.     buffer[0] = ' ';
  1407.     buffer[1] = ' ';
  1408.     buffer[2] = 0;
  1409.  
  1410.     Font_Engine_1(x,y,0,0,buffer,destination);
  1411.  
  1412. // done!
  1413.  
  1414. } // end Tech_Print
  1415.  
  1416. //////////////////////////////////////////////////////////////////////////////
  1417.  
  1418. void Font_Engine_1(int x,int y,
  1419.                    int font,int color,
  1420.                    char *string,unsigned char far *destination)
  1421. {
  1422. // this function prints a string out using one of the graphics fonts that
  1423. // we have drawn, note this first version doesn't use the font field, but
  1424. // we'll throw it in to keep the interface open for a future version
  1425.  
  1426. static int font_loaded=0;   // this is used to track the first time the
  1427.                             // function is loaded
  1428.  
  1429. int index,    // loop index
  1430.     c_index,  // character index
  1431.     length;   // used to compute lengths of strings
  1432.  
  1433. // test if this is the first time this function is called, if so load the
  1434. // font
  1435.  
  1436. if (!font_loaded)
  1437.    {
  1438.  
  1439.    // load the 4x7 tech font
  1440.  
  1441.    PCX_Init((pcx_picture_ptr)&image_pcx);
  1442.    PCX_Load("blazefnt.pcx", (pcx_picture_ptr)&image_pcx,1);
  1443.  
  1444.    // allocate memory for each bitmap and load character
  1445.  
  1446.    for (index=0; index<NUM_TECH_FONT; index++)
  1447.        {
  1448.        // allocate memory for charcter
  1449.  
  1450.        Bitmap_Allocate((bitmap_ptr)&tech_font[index],
  1451.                        TECH_FONT_WIDTH,TECH_FONT_HEIGHT);
  1452.  
  1453.        // set size of character
  1454.  
  1455.        tech_font[index].width = TECH_FONT_WIDTH;
  1456.        tech_font[index].height= TECH_FONT_HEIGHT;
  1457.  
  1458.        // extract bitmap from PCX buffer
  1459.  
  1460.        tech_font[index].x = 1 + (index % 16) * (TECH_FONT_WIDTH+1);
  1461.        tech_font[index].y = 1 + (index / 16) * (TECH_FONT_HEIGHT+1);
  1462.  
  1463.        Bitmap_Get((bitmap_ptr)&tech_font[index],
  1464.                   (unsigned char far *)image_pcx.buffer);
  1465.  
  1466.        } // end for index
  1467.    // font is loaded, delete pcx file and set flag
  1468.  
  1469.    PCX_Delete((pcx_picture_ptr)&image_pcx);
  1470.  
  1471.    font_loaded=1;
  1472.  
  1473.    } // end if first time
  1474. else
  1475.    {
  1476.    // print the sent string
  1477.  
  1478.    // pre-compute length of string
  1479.  
  1480.    length=strlen(string);
  1481.  
  1482.    // print the string character by character
  1483.  
  1484.    for (index=0; index<length; index++)
  1485.        {
  1486.        // extract the character index from the space character
  1487.  
  1488.        c_index = string[index] - ' ';
  1489.  
  1490.        // set bitmap position
  1491.  
  1492.        tech_font[c_index].y = y;
  1493.        tech_font[c_index].x = x;
  1494.  
  1495.        // display bitmap
  1496.  
  1497.        Bitmap_Put((bitmap_ptr)&tech_font[c_index],
  1498.                   (unsigned char far*)destination,0);
  1499.  
  1500.        // move to next character position
  1501.  
  1502.        x+=(TECH_FONT_WIDTH+1);
  1503.  
  1504.        } // end for index
  1505.  
  1506.    } // end else print string
  1507.  
  1508. } // end Font_Engine_1
  1509.  
  1510. /////////////////////////////////////////////////////////////////////////////
  1511.  
  1512. void Clear_Display(int color)
  1513. {
  1514. // this function fills the setup display screen with a color
  1515.  
  1516. int y;
  1517.  
  1518. // clear display with horizontal lines
  1519.  
  1520. for (y=DISPLAY_Y; y<DISPLAY_Y + DISPLAY_HEIGHT; y++)
  1521.      Line_H(DISPLAY_X,DISPLAY_X+DISPLAY_WIDTH-1,y,color);
  1522.  
  1523. } // end Clear_Display
  1524.  
  1525. /////////////////////////////////////////////////////////////////////////////
  1526.  
  1527. void Intro_Title(void)
  1528. {
  1529.  
  1530. // load in the starblazer title screen
  1531.  
  1532. PCX_Init((pcx_picture_ptr)&image_pcx);
  1533. PCX_Load("blazeint.pcx",(pcx_picture_ptr)&image_pcx,1);
  1534.  
  1535. // done with data so delete it
  1536.  
  1537. PCX_Delete((pcx_picture_ptr)&image_pcx);
  1538.  
  1539. // show the PCX buffer
  1540.  
  1541. PCX_Show_Buffer((pcx_picture_ptr)&image_pcx);
  1542.  
  1543. // do special effects
  1544.  
  1545. // wait for a sec
  1546.  
  1547. Time_Delay(10);
  1548.  
  1549. Do_Starburst();
  1550.  
  1551. Time_Delay(50);
  1552.  
  1553. } // end Intro_Title
  1554.  
  1555. //////////////////////////////////////////////////////////////////////////////
  1556.  
  1557. void Closing_Screen(void)
  1558. {
  1559. // this function prints the credits
  1560.  
  1561. // blank the screen
  1562.  
  1563. Fill_Screen(0);
  1564.  
  1565. // restore pallete
  1566.  
  1567. Write_Palette(0,255,(RGB_palette_ptr)&game_palette);
  1568.  
  1569. if (music_enabled)
  1570.    {
  1571.    Music_Stop();
  1572.    Music_Play((music_ptr)&song,10);
  1573.    }
  1574.  
  1575. // draw the credits
  1576.  
  1577. Tech_Print(10,50,"MUSICAL MASTERY BY",video_buffer);
  1578. Time_Delay(20);
  1579. Tech_Print(20,60,"DEAN HUDSON OF",video_buffer);
  1580. Time_Delay(20);
  1581. Tech_Print(30,70,"ECLIPSE PRODUCTIONS",video_buffer);
  1582. Time_Delay(20);
  1583.  
  1584. Tech_Print(10,100,"MIDPAK INSTRUMENTATION CONSULTING BY",video_buffer);
  1585. Time_Delay(20);
  1586. Tech_Print(20,110,"ROB WALLACE OF",video_buffer);
  1587. Time_Delay(20);
  1588. Tech_Print(30,120,"WALLACE MUSIC & SOUND",video_buffer);
  1589.  
  1590. // wait a sec
  1591.  
  1592. Time_Delay(125);
  1593.  
  1594. // fade away
  1595.  
  1596. Screen_Transition(SCREEN_DARKNESS);
  1597.  
  1598. } // end Closing_Screen
  1599.  
  1600. //////////////////////////////////////////////////////////////////////////////
  1601.  
  1602. void Intro_Waite(void)
  1603. {
  1604.  
  1605. // load in the waite group title screen
  1606.  
  1607. PCX_Init((pcx_picture_ptr)&image_pcx);
  1608. PCX_Load("waite.pcx",(pcx_picture_ptr)&image_pcx,1);
  1609.  
  1610. // done with data so delete it
  1611.  
  1612. PCX_Delete((pcx_picture_ptr)&image_pcx);
  1613.  
  1614. // show the PCX buffer
  1615.  
  1616. PCX_Show_Buffer((pcx_picture_ptr)&image_pcx);
  1617.  
  1618. // do special effects
  1619.  
  1620. // wait for a sec
  1621.  
  1622. Time_Delay(40);
  1623.  
  1624. Screen_Transition(SCREEN_WHITENESS);
  1625.  
  1626. // blank the screen
  1627.  
  1628. Fill_Screen(0);
  1629.  
  1630. } // end Intro_Waite
  1631.  
  1632. //////////////////////////////////////////////////////////////////////////////
  1633.  
  1634. void Intro_Controls(void)
  1635. {
  1636. // this function displays the controls screen
  1637.  
  1638. // load in the starblazer controls screen
  1639.  
  1640. PCX_Init((pcx_picture_ptr)&image_controls);
  1641. PCX_Load("blazecon.pcx",(pcx_picture_ptr)&image_controls,1);
  1642.  
  1643. // copy controls data to video buffer
  1644.  
  1645. PCX_Show_Buffer((pcx_picture_ptr)&image_controls);
  1646.  
  1647. // scan under button sprite and draw
  1648.  
  1649. Sprite_Under((sprite_ptr)&button_1,video_buffer);
  1650. Sprite_Draw((sprite_ptr)&button_1,video_buffer,1);
  1651.  
  1652. // delete pcx file
  1653.  
  1654. PCX_Delete((pcx_picture_ptr)&image_controls);
  1655.  
  1656. } // end Intro_Controls
  1657.  
  1658. ////////////////////////////////////////////////////////////////////////////
  1659.  
  1660. void Intro_Briefing(void)
  1661. {
  1662. // this function displays the controls screen
  1663.  
  1664. int done=0, // exit flag
  1665.     page=0, // current page user is reading
  1666.     index;  // looping variable
  1667.  
  1668. // load in the starblazer controls screen
  1669.  
  1670. PCX_Init((pcx_picture_ptr)&image_controls);
  1671. PCX_Load("blazeins.pcx",(pcx_picture_ptr)&image_controls,1);
  1672.  
  1673. // copy controls data to video buffer
  1674.  
  1675. PCX_Show_Buffer((pcx_picture_ptr)&image_controls);
  1676.  
  1677. // delete pcx file
  1678.  
  1679. PCX_Delete((pcx_picture_ptr)&image_controls);
  1680.  
  1681. // display the first page
  1682.  
  1683. for (index=0; index<NUM_LINES_PAGE; index++)
  1684.     Font_Engine_1(78,24+index*8,0,0,instructions[index+page*17],video_buffer);
  1685.  
  1686. // enter main event loop
  1687.  
  1688. while(!done)
  1689.      {
  1690.      // has the user pressed a key
  1691.  
  1692.      if (keys_active>0)
  1693.         {
  1694.  
  1695.         if (keyboard_state[MAKE_UP])
  1696.            {
  1697.            // page up
  1698.  
  1699.            if (--page<0)
  1700.               page = 0;
  1701.  
  1702.            // press button
  1703.  
  1704.            button_3.x = 185;
  1705.            button_3.y = 170;
  1706.            button_3.curr_frame = 3;
  1707.  
  1708.            Sprite_Draw((sprite_ptr)&button_3,video_buffer,1);
  1709.  
  1710.            Digital_FX_Play(BLZKEY_VOC,2);
  1711.  
  1712.            Time_Delay(2);
  1713.  
  1714.            button_3.curr_frame = 2;
  1715.  
  1716.            Sprite_Draw((sprite_ptr)&button_3,video_buffer,1);
  1717.  
  1718.            } // end if up
  1719.  
  1720.         if (keyboard_state[MAKE_DOWN])
  1721.            {
  1722.            // page down
  1723.  
  1724.            if (++page>=NUM_PAGES)
  1725.               page = NUM_PAGES-1;
  1726.  
  1727.  
  1728.            // press button
  1729.  
  1730.            button_3.x = 106;
  1731.            button_3.y = 170;
  1732.            button_3.curr_frame = 1;
  1733.  
  1734.            Sprite_Draw((sprite_ptr)&button_3,video_buffer,1);
  1735.  
  1736.            Digital_FX_Play(BLZKEY_VOC,2);
  1737.  
  1738.            Time_Delay(2);
  1739.  
  1740.            button_3.curr_frame = 0;
  1741.  
  1742.            Sprite_Draw((sprite_ptr)&button_3,video_buffer,1);
  1743.  
  1744.            } // end if down
  1745.  
  1746.         if (keyboard_state[MAKE_ESC])
  1747.            {
  1748.            done=1;
  1749.            } // end if esc
  1750.  
  1751.         // refresh display
  1752.  
  1753.         for (index=0; index<NUM_LINES_PAGE; index++)
  1754.             Font_Engine_1(78,24+index*8,0,0,instructions[index+page*17],video_buffer);
  1755.  
  1756.         } // end if a key has been pressed
  1757.  
  1758.      // do the scrolling lite thing
  1759.  
  1760.      Panel_FX();
  1761.  
  1762.      // wait a sec
  1763.  
  1764.      Time_Delay(1);
  1765.  
  1766.      } // end main while
  1767.  
  1768. } // end Intro_Briefing
  1769.  
  1770. ////////////////////////////////////////////////////////////////////////////
  1771.  
  1772. void Load_Explosions(void)
  1773. {
  1774. // this function loads the bitmap explosions
  1775.  
  1776. int index,  // looping variable
  1777.     frames; // looping variable
  1778.  
  1779. // load the imagery for the explosions
  1780.  
  1781. PCX_Init((pcx_picture_ptr)&image_pcx);
  1782. PCX_Load("blazeexp.pcx", (pcx_picture_ptr)&image_pcx,1);
  1783.  
  1784. // load each explosion in
  1785.  
  1786. for (index=0; index<NUM_EXPLOSIONS; index++)
  1787.     {
  1788.     // initialize each sprite
  1789.  
  1790.     Sprite_Init((sprite_ptr)&explosions[index],0,0,28,22,0,0,0,0,0,0);
  1791.  
  1792.     // extract the animation frames
  1793.  
  1794.     for (frames=0; frames<NUM_EXPLOSION_FRAMES; frames++)
  1795.         PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&explosions[index],frames,frames,0);
  1796.  
  1797.     } // end index
  1798.  
  1799. // delete the pcx file
  1800.  
  1801. PCX_Delete((pcx_picture_ptr)&image_pcx);
  1802.  
  1803. } // end Load_Explosions
  1804.  
  1805. ////////////////////////////////////////////////////////////////////////////
  1806.  
  1807. void Load_Icons(void)
  1808. {
  1809. // this function loads various icons for the game
  1810.  
  1811. int index; // looping variable
  1812.  
  1813. // load the imagery for the control buttons on the setup screen
  1814.  
  1815. PCX_Init((pcx_picture_ptr)&image_pcx);
  1816. PCX_Load("blazebt1.pcx", (pcx_picture_ptr)&image_pcx,1);
  1817.  
  1818. // intialize the button sprite
  1819.  
  1820. Sprite_Init((sprite_ptr)&button_1,118-10,63,10,8,0,0,0,0,0,0);
  1821.  
  1822. button_1.counter_1 = 0;  // button is on the 0th element in the list
  1823.  
  1824. // extract the bitmaps for the button, there are 4 animation cells
  1825.  
  1826. for (index=0; index<4; index++)
  1827.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&button_1,index,index,0);
  1828.  
  1829. // load in display selection buttons
  1830.  
  1831. // intialize the button sprite
  1832.  
  1833. Sprite_Init((sprite_ptr)&button_2,0,DISPLAY_Y+DISPLAY_HEIGHT-6,10,8,0,0,0,0,0,0);
  1834.  
  1835. // extract the bitmaps for the button, there are 2 animation cells
  1836.  
  1837. for (index=0; index<2; index++)
  1838.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&button_2,index,index,1);
  1839.  
  1840. // done with this PCX file so delete memory associated with it
  1841.  
  1842. PCX_Delete((pcx_picture_ptr)&image_pcx);
  1843.  
  1844.  
  1845. // load the imagery for the briefing control buttons
  1846.  
  1847. PCX_Init((pcx_picture_ptr)&image_pcx);
  1848. PCX_Load("blazebt3.pcx", (pcx_picture_ptr)&image_pcx,1);
  1849.  
  1850. // intialize the button sprite
  1851.  
  1852. Sprite_Init((sprite_ptr)&button_3,0,0,42,12,0,0,0,0,0,0);
  1853.  
  1854. // extract the bitmaps for the button, there are 4 animation cells
  1855.  
  1856. for (index=0; index<4; index++)
  1857.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&button_3,index,index,0);
  1858.  
  1859. // done with this PCX file so delete memory associated with it
  1860.  
  1861. PCX_Delete((pcx_picture_ptr)&image_pcx);
  1862.  
  1863. // load the imagery for the display bitmaps
  1864.  
  1865. PCX_Init((pcx_picture_ptr)&image_pcx);
  1866. PCX_Load("blazedis.pcx", (pcx_picture_ptr)&image_pcx,1);
  1867.  
  1868. // intialize the display sprite
  1869.  
  1870. Sprite_Init((sprite_ptr)&displays,DISPLAY_X,DISPLAY_Y+6,72,20,0,0,0,0,0,0);
  1871.  
  1872. // extract the bitmaps for the display bitmaps, there are 2 images
  1873.  
  1874. for (index=0; index<2; index++)
  1875.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&displays,index,index,0);
  1876.  
  1877. // done with this PCX file so delete memory associated with it
  1878.  
  1879. PCX_Delete((pcx_picture_ptr)&image_pcx);
  1880.  
  1881. } // end Load_Icons
  1882.  
  1883. ////////////////////////////////////////////////////////////////////////////
  1884.  
  1885. void Load_Ships(void)
  1886. {
  1887. int index; // looping variable
  1888.  
  1889. // load the imagery for the local ships
  1890.  
  1891. PCX_Init((pcx_picture_ptr)&image_pcx);
  1892. PCX_Load("blazeshl.pcx", (pcx_picture_ptr)&image_pcx,1);
  1893.  
  1894. // load in the imagery for the local gryfon and raptor
  1895.  
  1896. Sprite_Init((sprite_ptr)&gryfon_l,0,0,22,18,0,0,0,0,0,0);
  1897. Sprite_Init((sprite_ptr)&raptor_l,0,0,22,18,0,0,0,0,0,0);
  1898.  
  1899. // there are 32 animation cells per ship
  1900.  
  1901. for (index=0; index<16; index++)
  1902.     {
  1903.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&gryfon_l,index,index%12,index/12);
  1904.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&raptor_l,index,index%12,2+index/12);
  1905.  
  1906.     // these frames are with engines on
  1907.  
  1908.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&gryfon_l,index+16,index%12,4+index/12);
  1909.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&raptor_l,index+16,index%12,4+2+index/12);
  1910.  
  1911.     } // end for index
  1912.  
  1913. PCX_Delete((pcx_picture_ptr)&image_pcx);
  1914.  
  1915. // now the remote gryfon and raptor
  1916.  
  1917. PCX_Init((pcx_picture_ptr)&image_pcx);
  1918. PCX_Load("blazeshr.pcx", (pcx_picture_ptr)&image_pcx,1);
  1919.  
  1920. Sprite_Init((sprite_ptr)&gryfon_r,0,0,22,18,0,0,0,0,0,0);
  1921. Sprite_Init((sprite_ptr)&raptor_r,0,0,22,18,0,0,0,0,0,0);
  1922.  
  1923.  
  1924. // there are 32 animation cells per ship
  1925.  
  1926. for (index=0; index<16; index++)
  1927.     {
  1928.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&gryfon_r,index,index%12,index/12);
  1929.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&raptor_r,index,index%12,2+index/12);
  1930.  
  1931.     // these frames are with engines on
  1932.  
  1933.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&gryfon_r,index+16,index%12,4+index/12);
  1934.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&raptor_r,index+16,index%12,4+2+index/12);
  1935.  
  1936.     } // end for index
  1937.  
  1938. // initialize the player and remote sprites
  1939.  
  1940. Sprite_Init((sprite_ptr)&players_ship,160-11,100-9,22,18,0,0,0,0,0,0);
  1941. Sprite_Init((sprite_ptr)&remotes_ship,0,0,22,18,0,0,0,0,0,0);
  1942.  
  1943. // intialize the starburst
  1944.  
  1945. Sprite_Init((sprite_ptr)&starburst,0,0,22,18,0,0,0,0,0,0);
  1946.  
  1947. // extract the bitmaps for the startburst, there are 6 animation cells
  1948.  
  1949. for (index=0; index<6; index++)
  1950.     PCX_Get_Sprite((pcx_picture_ptr)&image_pcx,(sprite_ptr)&starburst,index,index,8);
  1951.  
  1952. PCX_Delete((pcx_picture_ptr)&image_pcx);
  1953.  
  1954. } // end Load_Ships
  1955.  
  1956. ////////////////////////////////////////////////////////////////////////////
  1957.  
  1958. void Do_Starburst(void)
  1959. {
  1960. int x,y,number,index;
  1961.  
  1962. // select a random number of startb bursts
  1963.  
  1964. number = 2+rand()%3;
  1965.  
  1966. for (index=0; index<number; index++)
  1967.     {
  1968.  
  1969.     // select position for starburst
  1970.  
  1971.     starburst.x=160+(rand()%140);
  1972.     starburst.y=80+rand()%20;
  1973.  
  1974.     Sprite_Under((sprite_ptr)&starburst,video_buffer);
  1975.  
  1976.     // do starburst
  1977.  
  1978.     for (starburst.curr_frame=0; starburst.curr_frame<6; starburst.curr_frame++)
  1979.         {
  1980.         Sprite_Erase((sprite_ptr)&starburst,video_buffer);
  1981.         Sprite_Under((sprite_ptr)&starburst,video_buffer);
  1982.         Sprite_Draw((sprite_ptr)&starburst,video_buffer,1);
  1983.  
  1984.         Time_Delay(1+rand()%2);
  1985.  
  1986.         } // end for starburst frame
  1987.  
  1988.         // erase the starburst
  1989.  
  1990.         Sprite_Erase((sprite_ptr)&starburst,video_buffer);
  1991.  
  1992.     } // end index
  1993.  
  1994. } // end Do_Starburst
  1995.  
  1996. ////////////////////////////////////////////////////////////////////////////
  1997.  
  1998. int Display_Select(int current)
  1999. {
  2000. // this function is used to select between two choices in the display window
  2001.  
  2002. // compute starting position of selection icon based on default selection
  2003.  
  2004. button_2.x = DISPLAY_X + 14 + current*40;
  2005.  
  2006. // scan under selection icon
  2007.  
  2008. Sprite_Under((sprite_ptr)&button_2,video_buffer);
  2009. Sprite_Draw((sprite_ptr)&button_2,video_buffer,1);
  2010.  
  2011. // until user exits process event loop
  2012.  
  2013. while(1)
  2014.      {
  2015.      // get input
  2016.  
  2017.      if (keys_active>0)
  2018.         {
  2019.  
  2020.         // what is user trying to do?
  2021.  
  2022.         if (keyboard_state[MAKE_RIGHT])
  2023.            {
  2024.            if (++current>1)
  2025.               current = 0;
  2026.  
  2027.            Digital_FX_Play(BLZKEY_VOC,2);
  2028.  
  2029.            Time_Delay(1);
  2030.  
  2031.            } // if if right
  2032.  
  2033.  
  2034.         else
  2035.         if (keyboard_state[MAKE_LEFT])
  2036.            {
  2037.            if (--current<0)
  2038.               current = 1;
  2039.  
  2040.            Digital_FX_Play(BLZKEY_VOC,2);
  2041.  
  2042.            Time_Delay(1);
  2043.  
  2044.            } // end if left
  2045.  
  2046.         else
  2047.         if (keyboard_state[MAKE_ESC])
  2048.            {
  2049.            // print that selection was aborted
  2050.  
  2051.            Clear_Display(0);
  2052.  
  2053.            Font_Engine_1(DISPLAY_X+2,DISPLAY_Y+2,0,0,
  2054.                          "ABORTED...",
  2055.                          video_buffer);
  2056.  
  2057.            Digital_FX_Play(BLZABRT_VOC,1);
  2058.  
  2059.            Time_Delay(5);
  2060.  
  2061.            Clear_Display(0);
  2062.  
  2063.            // return selection aborted
  2064.  
  2065.            return(-1);
  2066.  
  2067.            } // end if esc
  2068.  
  2069.         else
  2070.         if (keyboard_state[MAKE_ENTER])
  2071.            {
  2072.            // carriage return, making selection
  2073.            // illuminate button for a second
  2074.  
  2075.            // draw button down
  2076.  
  2077.            button_2.curr_frame = 1;
  2078.            Sprite_Draw((sprite_ptr)&button_2,video_buffer,1);
  2079.  
  2080.            Digital_FX_Play(BLZKEY_VOC,2);
  2081.  
  2082.            Time_Delay(5);
  2083.  
  2084.            // now draw button up
  2085.  
  2086.            button_2.curr_frame = 0;
  2087.            Sprite_Draw((sprite_ptr)&button_2,video_buffer,1);
  2088.  
  2089.            Clear_Display(0);
  2090.  
  2091.            Font_Engine_1(DISPLAY_X+2,DISPLAY_Y+2,0,0,
  2092.                          "SELECTION",
  2093.                          video_buffer);
  2094.  
  2095.            Font_Engine_1(DISPLAY_X+2,DISPLAY_Y+2+8,0,0,
  2096.                          "RECORDED",
  2097.                          video_buffer);
  2098.  
  2099.            Digital_FX_Play(BLZSEL_VOC,1);
  2100.  
  2101.            Time_Delay(5);
  2102.  
  2103.            Clear_Display(0);
  2104.  
  2105.            // return the selection
  2106.  
  2107.            return(current);
  2108.  
  2109.            } // end if enter
  2110.  
  2111.         // erase selection icon
  2112.  
  2113.         Sprite_Erase((sprite_ptr)&button_2,video_buffer);
  2114.  
  2115.         // compute x position
  2116.  
  2117.         button_2.x = DISPLAY_X + 14 + current*40;
  2118.  
  2119.         // scan under and draw selection icon
  2120.  
  2121.         Sprite_Under((sprite_ptr)&button_2,video_buffer);
  2122.         Sprite_Draw((sprite_ptr)&button_2,video_buffer,1);
  2123.  
  2124.         } // end if kbhit
  2125.  
  2126.      // perform special effects
  2127.  
  2128.      Panel_FX();
  2129.  
  2130.      // wait a bit
  2131.  
  2132.      Time_Delay(1);
  2133.  
  2134.      } // end while
  2135.  
  2136. } // Display_Select
  2137.  
  2138. ///////////////////////////////////////////////////////////////////////////////
  2139.  
  2140. void Copy_Frames(sprite_ptr dest, sprite_ptr source)
  2141. {
  2142. // this function is used to copy the image frames from one sprite to another
  2143.  
  2144. int index; // looping variable
  2145.  
  2146. for (index=0; index<source->num_frames; index++)
  2147.     {
  2148.     // assign next frame
  2149.  
  2150.     dest->frames[index] = source->frames[index];
  2151.  
  2152.     } // end for index
  2153.  
  2154. // set up dest fields
  2155.  
  2156. dest->num_frames = source->num_frames;
  2157. dest->curr_frame = 0;
  2158.  
  2159. } // end Copy_Frames
  2160.  
  2161. /////////////////////////////////////////////////////////////////////////////
  2162.