home *** CD-ROM | disk | FTP | other *** search
/ Aztec Shareware Collection / CHILDREN.ISO / firework / explod.c < prev    next >
Text File  |  1990-01-21  |  31KB  |  965 lines

  1. /****************************************************************************
  2. * EXPLOD.C - simple fireworks for Hercules/CGA/EGA
  3. *
  4. * This program was developed with Datalight C 2.20 and Arrowsoft ASM 1.00d.
  5. * (It also works with Turbo C 1.5/MASM 5.0 with one EQU change in EXPA.ASM).
  6. * The code should be compilable by most other C compilers. However, the
  7. * segment names in EXPA.ASM will have to be changed to suit the compiler.
  8. *
  9. * To compile:
  10. *       asm expa ;            OR        masm /mx expa ;
  11. *       dlc explod.c expa.obj            (don't forget the /mx !!!)
  12. *                        tcc explod.c expa.obj
  13. * (C) 1989 Dennis Lo
  14. * You are free to use and distribute this source code, provided that the
  15. * authors' names remain in the code, and that both the source and executables 
  16. * of modified versions are clearly distinguished from the originals with
  17. * descriptive messages.
  18. *
  19. * Change Log
  20. * 89/06/24 Dennis Lo         V1.0 Initial release.
  21. * 89/07/03 Dennis Lo         V1.1 Added cmd line options.  Code cleanups.
  22. * 89/07/26 Erik Liljencrantz Added EGA support with autodetection
  23. *                            Restores previous CRT textmode on exit
  24. * 89/08/19 Dennis Lo         V1.2 Code cleanups.  Changed references to frames
  25. *                 table to use segment and offsets so that multiple
  26. *                 frames tables can be supported in the future 
  27. *                 without switching to a large data memory model.
  28. * 89/09/03 Erik Liljencrantz Added VGA support with autodetection
  29. * 90/01/20 Dennis Lo         V2.0 Allow multiple explosion frame tables.
  30. *                 Read in explosion shape tables from data files.
  31. *                 Added -f and keypress options.  Split part of
  32. *                 functionality into expgen.c.  Keyboard control.
  33. *                 -n option.
  34. ****************************************************************************/
  35.  
  36. #include <stdio.h>
  37. #include <ctype.h>
  38. #ifndef DLC
  39. #   include <dir.h>
  40. #endif
  41. #include <dos.h>
  42. #include "explod.h"
  43.  
  44. #define PUT(s) fputs(s,stdout)
  45.  
  46. /*=====================================================================
  47.  * External assembler routines.  
  48.  *=====================================================================
  49.  */
  50. /* Datalight C does not prepend underscores to external names */
  51. #ifdef DLC
  52. #   define DosAlloc    _DosAlloc
  53. #   define DosFree    _DosFree
  54. #   define DosMemLeft    _DosMemLeft
  55. #   define ChkKey    _ChkKey
  56. #   define gr_card    _gr_card
  57. #   define gr_setcard    _gr_setcard
  58. #   define gr_gmode    _gr_gmode
  59. #   define gr_tmode    _gr_tmode
  60. #   define gr_addr    _gr_addr
  61. #   define gr_store_pt    _gr_store_pt
  62. #   define gr_frplot    _gr_frplot
  63. #endif
  64.  
  65. extern unsigned short DosAlloc();
  66. extern unsigned short DosFree();
  67. extern unsigned short DosMemLeft();
  68. extern int ChkKey();
  69. extern int gr_card();
  70. extern gr_setcard();
  71. extern gr_gmode();
  72. extern gr_tmode();
  73. extern int gr_addr();
  74. extern gr_store_pt();
  75. extern gr_frplot();
  76. #define DEADPOINT 32767    /* this must match the definition in expa.asm */
  77.  
  78. /*=====================================================================
  79.  * Configuration Parameters
  80.  *=====================================================================
  81.  */
  82. /* Video parameters */
  83. static int Interlace_factor;
  84. static int Screen_xsize;
  85. static int Screen_ysize;
  86.  
  87. /* Animation parameters */
  88. static int Simul_exps = -1;      /* # of simultaneous explosions */
  89. static int Delay_factor = 0;    /* amount to delay after each frame */
  90.  
  91. /* Explosion parameters */
  92. static int Gravity = 0;        /* Gravity override */
  93. static int Wind = 0;        /* Wind override */
  94. static int Centre_x;        /* Coords of centre of screen */
  95. static int Centre_y;
  96. static unsigned int Centre_y_mask;
  97.  
  98. /* Explosion control parameters */
  99. static int Leave_trail = 0;    /* If non-0 then don't erase explosions */
  100. static int Event_limit = 0;    /* # of exp events before exiting */
  101. static int Total_frames = 0;    /* used to find avg #frames per explosion */
  102.  
  103. /*=====================================================================
  104.  * Explosion Creation table:
  105.  * Temporarily keeps track of the parameters and points of an explosion
  106.  * while its frames are being calculated.
  107.  *=====================================================================
  108.  */
  109. /* 16.16 fixed point type: overlays a 32-bit long with its 16-bit
  110.    whole number part and its 16-bit fractional part */
  111. typedef union         
  112. {
  113.     long l;
  114.     struct             /* Note: this is endian dependent */
  115.     {
  116.         unsigned short ls;
  117.         short ms;
  118.     } s;
  119. } fixed; 
  120.  
  121. /* Structure of an explosion point at explosion creation */
  122. typedef struct 
  123. {
  124.     fixed x, y;                 /* location */
  125.     fixed xv, yv;               /* velocity */
  126.     fixed xa, ya;               /* acceleration */
  127.     int alive;                  /* liveness: 0=dead */
  128. } exp_pt_type;
  129.  
  130. /* Explosion creation table */
  131. exp_pt_type Pt_table [MAX_POINTS+1];
  132.  
  133. /*=====================================================================
  134.  * Explosion type descriptor - contains all information in a ready-to-
  135.  * playback explosion, including a pointer to its frames table.  The
  136.  * frames table contains an array of MAX_FRAMES frames.  Each frame is
  137.  * one frame in the animation of the explosion, and consists of 
  138.  * MAX_POINTS structures of the form {offset (2 bytes), value (1 byte)}.
  139.  * The frame table structure treated only as a block of memory by the
  140.  * C code, so it is not explicitly declared. (It cannot be declared
  141.  * in C in a portable way due to structure alignment padding.)
  142.  *=====================================================================
  143.  */
  144. #define FRAME_PT_SIZE 3        /* size of one point in the frame table */
  145. #define MAX_EXP_DESC    30    /* Max # of explosion types */
  146.  
  147. /* Explosion descriptor type */
  148. typedef struct
  149. {
  150.     int nframes;
  151.     int npoints;
  152.     int xsize;
  153.     int ysize;
  154.     int gravity;
  155.     int wind;
  156.     int trail_len;
  157.     unsigned short frame_seg;
  158.     unsigned short frame_ofs;
  159. } exp_desc_type;
  160.  
  161. /* Array of explosion descriptors for the explosions read in from disk */
  162. static exp_desc_type    Exp_desc [MAX_EXP_DESC];/* Explosion descriptors */
  163. static int        Num_exp_desc;        /* # of explosions read in */
  164.  
  165.  
  166. /*=====================================================================
  167.  * Explosion Event Table - each entry controls one instance of an 
  168.  * active explosion event during playback.
  169.  *=====================================================================
  170.  */
  171. #define MAX_EXPS 48     /* max # of simultaneously active explosion events */
  172.  
  173. /* Explosion events table */
  174. typedef struct 
  175. {
  176.     int centre;             /* addr of centre of explosion */
  177.     int frame_num;          /* frame #. Dead if -1 */
  178.     exp_desc_type *exp_desc;    /* explosion type */
  179. } exp_event_type;
  180. static exp_event_type Exp_ev_tab [MAX_EXPS]; 
  181.  
  182.  
  183.  
  184. /*====================================================================
  185.  * Return a random number between 0..maxval
  186.  *====================================================================
  187.  */
  188. int
  189. rnd (maxval)
  190. int maxval;
  191. {
  192. #   define MAX_RAND 32767       /* max val returned by rand() */
  193.     long l;
  194.  
  195.     l = (long) maxval * rand() / MAX_RAND;
  196.     return ((int) l);
  197. }
  198.  
  199. /*====================================================================
  200.  * Read a number from a string - replaces the need for sscanf() which
  201.  * reduces the size of the executable by about 2.5K.
  202.  * Returns a ptr to the next word in the string.
  203.  *====================================================================
  204.  */
  205. char *
  206. str_to_num (str, num)
  207. char *str;    /* IN: ptr to string containing the number */
  208. int *num;    /* OUT: the number read */
  209. {
  210.     /* Skip leading spaces */
  211.     while (*str  &&  *str == ' ') str++;
  212.  
  213.     *num = atoi (str);
  214.  
  215.     /* Skip non spaces (skip past the number) */
  216.     while (*str  &&  *str != ' ') str++;
  217.  
  218.     return (str);
  219. }
  220.  
  221. /*====================================================================
  222.  * Local memory allocator:
  223.  * MemInit() - allocates as much memory from DOS as possible.
  224.  *           Return 1 if successful, 0 if not.
  225.  * MemAlloc() - allocates a block of memory to the caller.
  226.  *        Returns segment addr of block, or 0 if failed.
  227.  * MemCleanup() - returns all memory to DOS.
  228.  *
  229.  * Why a local memory allocator?  malloc() won't work in the small 
  230.  * memory model because we need >64K of memory.  DOS's malloc()
  231.  * (int21 ah=48h) seems to waste a lot of memory when called several
  232.  * times in a row -- it was leaving ~5K gaps between each allocated
  233.  * block for reasons that I don't understand.  I tried a DOS realloc()
  234.  * (int21 ah=4ah) but that didn't help.  After a month of playing with
  235.  * it I gave up in disgust and wrote these routines instead.
  236.  *====================================================================
  237.  */
  238. unsigned mem_start;
  239. unsigned mem_curr;
  240. unsigned mem_left;
  241.  
  242. int
  243. MemInit()
  244. {
  245.     /* 
  246.      * Allocate all available memory from DOS:
  247.      * Try to allocate 640K at first so DOS will return the actual 
  248.      * amount available.
  249.      */
  250.     mem_left = DosMemLeft ();
  251.     if (mem_left == 0)
  252.     {
  253.     PUT ("DosMemLeft failed!\n");
  254.     return (0);
  255.     }
  256.     mem_start = DosAlloc (mem_left);
  257.     if (mem_start == 0)
  258.     {
  259.     PUT ("MemInit: DosAlloc failed!\n");
  260.     return (0);
  261.     }
  262.     mem_curr = mem_start;
  263.     return (1);
  264. }
  265.  
  266. int
  267. MemAlloc (size)
  268. unsigned size;        /* size in bytes */
  269. {
  270.     int addr;
  271.  
  272.     /* Convert the requested size to # of paragraphs */
  273.     size = (size + 15) >> 4;
  274.  
  275.     if (size > mem_left)
  276.     return (0);
  277.     addr = mem_curr;
  278.     mem_curr += size;
  279.     mem_left -= size;
  280.  
  281.     return (addr);
  282. }
  283.  
  284. MemCleanup()
  285. {
  286.     if (DosFree (mem_start) != 0)
  287.     PUT ("DosFree failed!\n");
  288. }
  289.  
  290.  
  291. /************************ User Interface ***************************/
  292.  
  293.  
  294. /*=====================================================================
  295.  * Return the path name portion of a filename.
  296.  *=====================================================================
  297.  */
  298. void
  299. GetPathName (filename, pathname)
  300. char *filename;        /* in */
  301. char *pathname;        /* out */
  302. {
  303.     char *path_end;
  304.     char path_len = 0;
  305.  
  306.     /* Scan backwards in the filename for the end of the path */
  307.     path_end = filename + strlen (filename);
  308.     while (path_end >= filename  &&  *path_end != ':'  &&
  309.        *path_end != '/'  &&  *path_end != '\\')
  310.     path_end--;
  311.  
  312.     /* Copy the path to the output string */
  313.     if (path_end >= filename)
  314.     {
  315.     path_len = path_end - filename;
  316.     strncpy (pathname, filename, path_len);
  317.     pathname [path_len++] = '\\';
  318.     }
  319.  
  320.     pathname [path_len] = '\0';
  321. }
  322.  
  323. /*=====================================================================
  324.  * Process command line arguments.  
  325.  * Returns 1 if successful, 0 if not.
  326.  * Also sets up explosion parameters according to video card type.
  327.  *=====================================================================
  328.  */
  329. int
  330. GetArgs (argc, argv)
  331.     int argc;
  332.     char *argv[];
  333. {
  334.     int video_type;
  335.     static char name [80];        /* Current data file name */
  336.     static char path [80];        /* Current path */
  337.  
  338.     /* Set video params */
  339.     video_type = gr_card ();        /* guess video card type */
  340.     SetVideoParams (video_type);         
  341.  
  342.     /* Print instructions if no command line parameters are given */
  343.     if (argc == 1)
  344.         Instructions();
  345.  
  346.     /*
  347.      * Loop to parse each command line parameter
  348.      */
  349.     while (--argc > 0) 
  350.     {
  351.         if (**++argv == '-') 
  352.         {
  353.             switch ((*argv)[1])
  354.             {
  355.                 case 'v':               /* -v: video card type (c, h)*/
  356.                     video_type = (*argv)[2];
  357.             SetVideoParams (video_type);         
  358.                     break;
  359.                 case 's':               /* -s: # of simultaneous explosions */
  360.                     Simul_exps = atoi ((*argv) + 2);
  361.                     break;
  362.                 case 'd':               /* -d: delay factor */
  363.                     Delay_factor = atoi ((*argv) + 2);
  364.                     break;
  365.                 case 'g':               /* -g: gravity override */
  366.                     Gravity = atoi ((*argv) + 2);
  367.                     break;
  368.                 case 'w':               /* -w: wind override */
  369.                     Wind = atoi ((*argv) + 2);
  370.                     break;
  371.         case 'f':        /* -f: fill screen with trails */
  372.             Leave_trail = 1;
  373.             break;
  374.         case 'n':        /* -n: total number of explosions */
  375.             Event_limit = atoi ((*argv) + 2);
  376.             break;
  377.                 default:
  378.                     PUT ("*** Invalid option: "); PUT (*argv); PUT ("\n");
  379.                     Instructions();
  380.                     return (0);
  381.             }
  382.         }
  383.     /*
  384.      * Else a filename argument:
  385.      *   Wildcard expand the filename and read in each of the resulting
  386.      *   explosion data files.
  387.      */
  388.     else
  389.     {
  390. #ifdef DLC
  391.         struct FIND *blk;
  392. #        define FFNAME blk->name
  393.  
  394.         GetPathName (*argv, path);
  395.         blk = findfirst (*argv, 0);
  396.         while (blk != NULL)
  397.         {
  398.         strcpy (name, path);
  399.         strcat (name, FFNAME);
  400.             PUT ("Reading "); PUT (name); PUT ("\n");
  401.             if (ReadDatFile (name, &Exp_desc[Num_exp_desc], Pt_table))
  402.             Num_exp_desc++;
  403.         blk = findnext ();
  404.         } 
  405. #else /* TURBOC */
  406.         struct ffblk blk;
  407. #        define FFNAME blk.ff_name
  408.         int more_files;
  409.  
  410.         GetPathName (*argv, path);
  411.         more_files = findfirst (*argv, &blk, 0);
  412.         while (more_files == 0)
  413.         {
  414.         strcpy (name, path);
  415.         strcat (name, FFNAME);
  416.             PUT ("Reading "); PUT (name); PUT ("\n");
  417.             if (ReadDatFile (name, &Exp_desc[Num_exp_desc], Pt_table))
  418.             Num_exp_desc++;
  419.         more_files = findnext (&blk);
  420.         } 
  421. #endif
  422.         }
  423.     }
  424.     return (1);
  425. }
  426.  
  427. /*=====================================================================
  428.  * Set video card-related parameters
  429.  *=====================================================================
  430.  */
  431. SetVideoParams (video_mode)
  432.     char video_mode;
  433. {
  434. #   define NUM_VIDEO_MODES 4
  435.     /*
  436.      * Default parameters for each video mode
  437.      */
  438.     static struct defaults_struct {
  439.     char video_mode;
  440.     int interlace_factor;
  441.     int screen_xsize;
  442.     int screen_ysize;
  443.     int simul_exps;
  444.     } defaults [NUM_VIDEO_MODES] = { 
  445.     'c', 2, 640, 200, 8,        /* cga */
  446.     'h', 4, 720, 348, 10,        /* hgc */
  447.     'e', 1, 640, 350, 10,        /* ega */
  448.         'v', 1, 640, 480, 12        /* vga */
  449.     };
  450.     int i;
  451.         
  452.     /* 
  453.      * Find the defaults table entry for the given video mode and set
  454.      * the video configuration parameters to the values in that entry.
  455.      */
  456.     for (i=0; i<NUM_VIDEO_MODES; i++)
  457.     {
  458.         if (video_mode == defaults[i].video_mode)
  459.     {
  460.         Interlace_factor = defaults [i].interlace_factor;
  461.         Screen_xsize = defaults [i].screen_xsize;
  462.         Screen_ysize = defaults [i].screen_ysize;
  463.  
  464.         /* If not specified on the command line then set to default */
  465.         if (Simul_exps == -1) 
  466.         Simul_exps = defaults [i].simul_exps;
  467.  
  468.         break;
  469.     }
  470.     }
  471.     Centre_y_mask = (0xffff - (Interlace_factor - 1));
  472.     Centre_x = (Screen_xsize / 2);
  473.     Centre_y = (Screen_ysize / 2) & Centre_y_mask;
  474.     gr_setcard (video_mode);
  475. }
  476.  
  477. /*=====================================================================
  478.  * Print instructions
  479.  *=====================================================================
  480.  */
  481. Instructions ()
  482. {
  483.     PUT ("EXPLOD 2.0          by Dennis Lo  90/01/20\n");
  484.     PUT ("Usage: explod [optional parameters] <data files>\n");
  485.     PUT ("Parameters can be one of\n");
  486.     PUT ("  -vC :C=video type.  CGA:'-vc'  HGC:'-vh'  EGA:'-ve'  VGA:'-vv'\n");
  487.     PUT ("       Default is auto-detect\n");
  488.     PUT ("  -sN :N = # of simultaneous explosions. Default: 10 (8 for CGA)\n");
  489.     PUT ("  -dN :N = Delay factor.  Default: 0\n");
  490.     PUT ("  -gN :N = Gravity.       Default: CGA=15 HGC=20 EGA=20\n");
  491.     PUT ("  -wN :N = Wind.          Default: 0\n");
  492.     PUT ("  -k  : Keyboard control - start explosions with keypresses.\n");
  493.     PUT ("  -f  : Fill screen with explosion trails.\n");
  494.     PUT ("  -nN :N = # of explosions before exiting.  Default: 0 (infinite)\n");
  495.     PUT ("eg. (for 12Mhz AT with VGA, in EGA mode): explod -ve -d6 *.dat\n");
  496.     PUT ("Press ESCAPE to return to DOS\n");
  497. }
  498.  
  499.  
  500.  
  501. /********************* Explosion event handling *************************/
  502.  
  503. /*=====================================================================
  504.  * Initialize the explosion events table 
  505.  *=====================================================================
  506.  */
  507. InitExpEvents ()
  508. {
  509.     int i;
  510.  
  511.     /* Set all events inactive */
  512.     for (i=0; i<MAX_EXPS; i++)
  513.         Exp_ev_tab[i].frame_num = -1;
  514. }
  515.  
  516. /*=====================================================================
  517.  * Add an explosion to the events table 
  518.  *=====================================================================
  519.  */
  520. StartExpEvent (x, y)
  521.     int x, y;
  522. {
  523.     int i;
  524.     exp_desc_type *e;
  525.  
  526.     y &= Centre_y_mask;
  527.  
  528.     /* Find the first free entry in the table */
  529.     for (i=0; i<MAX_EXPS; i++)
  530.         if (Exp_ev_tab[i].frame_num == -1) 
  531.             break;
  532.  
  533.     /* don't do anything if table is full */
  534.     if (i >= MAX_EXPS) 
  535.         return;
  536.  
  537.     Exp_ev_tab[i].centre = gr_addr (x, y);
  538.     Exp_ev_tab[i].frame_num = 0;
  539.     e = &Exp_desc [rnd (Num_exp_desc)];
  540.     Exp_ev_tab[i].exp_desc = e;
  541.  
  542.     /* Display the first frame */
  543.     gr_frplot (e->npoints, e->frame_seg,  e->frame_ofs,
  544.         Exp_ev_tab[i].centre, 1);
  545. }
  546.  
  547. /*=====================================================================
  548.  * Play back 1 frame of each event in the explosion events table
  549.  *=====================================================================
  550.  */
  551. PlayExpEvents ()
  552. {
  553.     int i, j;
  554.     unsigned short frame_seg;
  555.     unsigned short frame_ofs;
  556.     unsigned short frame_i_addr;
  557.     exp_event_type *event;
  558.     exp_desc_type *e;
  559.  
  560.     /*
  561.      * Loop once for each event in the events table:
  562.      */
  563.     for (i=0; i < MAX_EXPS; i++)
  564.     {
  565.     event = &Exp_ev_tab[i];
  566.     e = event->exp_desc;
  567.  
  568.         if (event->frame_num != -1)    /* if the event is active */
  569.         {
  570.         frame_seg = e->frame_seg;
  571.         frame_ofs = e->frame_ofs;
  572.  
  573.             /* if finished last frame of this explosion event */
  574.             if (++event->frame_num == (e->nframes + e->trail_len))
  575.             {
  576.                 /* turn off the final frame's points */
  577.         if (!Leave_trail)
  578.                     gr_frplot (e->npoints, frame_seg, 
  579.                 frame_ofs + (event->frame_num - e->trail_len-1) *
  580.                 e->npoints * FRAME_PT_SIZE, Exp_ev_tab[i].centre, 0);
  581.  
  582.                 /* free the current event's in the explosion events table */
  583.                 event->frame_num = -1;
  584.             }
  585.  
  586.             else
  587.             {
  588.                 /* Turn off previous frame's points (unless no prev frame) */
  589.                 if (event->frame_num - e->trail_len - 1 >= 0  &&
  590.             !Leave_trail)
  591.                     gr_frplot (e->npoints, frame_seg,
  592.             frame_ofs + (event->frame_num - e->trail_len-1) *
  593.             e->npoints * FRAME_PT_SIZE, Exp_ev_tab[i].centre, 0);
  594.  
  595.                 /* Turn on current frame's points */
  596.                 if (event->frame_num < e->nframes)
  597.                     gr_frplot (e->npoints, frame_seg, 
  598.             frame_ofs + event->frame_num * 
  599.             e->npoints * FRAME_PT_SIZE, Exp_ev_tab[i].centre, 1);
  600.             }
  601.         }
  602.     }
  603. }
  604.  
  605.  
  606.  
  607. /*********************** Explosion Creation ************************/
  608.  
  609. /*====================================================================
  610.  * Generate the frames of an explosion, storing them in a frame table
  611.  * at the given address.
  612.  *====================================================================
  613.  */
  614. FrameInit (e, pt_table)
  615.     exp_desc_type *e;
  616.     exp_pt_type   pt_table[];
  617. {
  618.     exp_pt_type       *curr_pt;         /* current explosion point */
  619.     unsigned       frame_i, i;         /* loop counters        */
  620.     unsigned short frame_i_addr;     /* addr of current frame    */
  621.     int           fade_window = 0;  /* fade counter (can fade if 0) */
  622.     unsigned short centre_addr = gr_addr (Centre_x, Centre_y);
  623.     unsigned short frame_seg   = e->frame_seg;
  624.     unsigned short frame_ofs   = e->frame_ofs;
  625.  
  626.     /*
  627.      * Loop once to create each frame of the explosion
  628.      */
  629.     for (frame_i = 0; frame_i < e->nframes; frame_i++)
  630.     {
  631.     /* Find the address of this frame in the frame table */
  632.     frame_i_addr = frame_ofs + (frame_i * e->npoints * FRAME_PT_SIZE);
  633.  
  634.         /*
  635.      * Loop once for each point in the explosion
  636.          */
  637.         for (i=0; i<e->npoints; i++)
  638.         {
  639.             curr_pt = &(pt_table[i]);
  640.  
  641.             /* If the point is dead, mark it as dead and go on to the next */
  642.             if (!curr_pt->alive)
  643.         {
  644.             gr_store_pt (DEADPOINT, 0, frame_seg, 
  645.                  /* below is &frame table [frame_i] [i] */
  646.                  frame_i_addr + (i * FRAME_PT_SIZE),  0);
  647.         continue;
  648.         }
  649.  
  650.             /* calculate the next position of the point */
  651.             curr_pt->x.l += curr_pt->xv.l;
  652.             curr_pt->y.l += curr_pt->yv.l;
  653.             curr_pt->xv.l += curr_pt->xa.l;
  654.             curr_pt->yv.l += curr_pt->ya.l;
  655.  
  656.         /* Store the point in the frame */
  657.         gr_store_pt (curr_pt->x.s.ms, curr_pt->y.s.ms, frame_seg, 
  658.         /* below is &frame table [frame_i] [i] */
  659.         frame_i_addr + (i * FRAME_PT_SIZE), 
  660.         centre_addr);
  661.  
  662.         /* 
  663.          * Do point fadeout - one point dies 1 out of 8 times here,
  664.          * starting at 60% of the way through the explosion.
  665.              */
  666.             fade_window = (fade_window + 1) & 7;
  667.         if (frame_i > (e->nframes * 3 / 5)  &&  fade_window == 0)
  668.                 curr_pt->alive = 0;
  669.         }
  670.     }
  671. }
  672.  
  673. /*====================================================================
  674.  * Read in an explosion description from the given disk file.
  675.  * Returns 1 if successful, 0 if not.
  676.  *====================================================================
  677.  */
  678. int
  679. ReadDatFile (filename, e, pt_table)
  680.     char *filename;
  681.     exp_desc_type *e;
  682.     exp_pt_type pt_table[];
  683. {
  684.     int    x1, y1, x2, y2;
  685.     long src_x, src_y, dest_x, dest_y;
  686.     long accel, vel;
  687.     int cx = Centre_x; 
  688.     int cy = Centre_y;
  689.     int i;
  690.     FILE *fp;
  691. #   define LINELEN 80
  692.     char linebuf [LINELEN];
  693.     char *lineptr;
  694.     unsigned short frame_buf_size;
  695.  
  696.     /* 
  697.      * Read in the explosion description file header information 
  698.      */
  699.     if ((fp = fopen (filename, "r")) == NULL)
  700.     {
  701.     PUT ("Error reading file "); PUT (filename); PUT ("\n");
  702.     return (0);
  703.     }
  704.     fgets (linebuf, LINELEN, fp);
  705.     str_to_num (linebuf, &e->npoints);
  706.     fgets (linebuf, LINELEN, fp);
  707.     str_to_num (linebuf, &e->nframes);
  708.     fgets (linebuf, LINELEN, fp);
  709.     str_to_num (linebuf, &e->gravity);
  710.     fgets (linebuf, LINELEN, fp);
  711.     str_to_num (linebuf, &e->wind);
  712.     fgets (linebuf, LINELEN, fp);
  713.     str_to_num (linebuf, &e->trail_len);
  714.  
  715.     /* 
  716.      * Perform any user-specified command-line parameter overrides 
  717.      */
  718.     if (Gravity != 0)
  719.     e->gravity = Gravity;
  720.     if (Wind != 0)
  721.     e->wind = Wind;
  722.  
  723.     /* 
  724.      * Allocate memory for the explosion's frames 
  725.      */
  726.     e->frame_ofs = 0;
  727.     frame_buf_size = (unsigned short) e->nframes * e->npoints * FRAME_PT_SIZE;
  728.     if ((e->frame_seg = MemAlloc (frame_buf_size)) == 0)
  729.     { 
  730.     PUT ("Not enough memory for file "); 
  731.     PUT (filename);
  732.     PUT ("\n");
  733.     return (1);
  734.     }
  735.  
  736.     /* 
  737.      * Read in the points for the explosion 
  738.      */
  739.     for (i=0; i<e->npoints; i++) 
  740.     {
  741.     /* read in a (srcx, srcy), (destx, desty) coordinate pair */
  742.         fgets (linebuf, LINELEN, fp);
  743.     lineptr = str_to_num (linebuf, &x1);
  744.     lineptr = str_to_num (lineptr, &y1);
  745.     lineptr = str_to_num (lineptr, &x2);
  746.     lineptr = str_to_num (lineptr, &y2);
  747.  
  748.         /* Convert to fixed pt. (Can't use shifts because they are unsigned) */
  749.         src_x = (x1 + cx) * 65536;
  750.         src_y = (y1 + cy) * 65536;
  751.         dest_x = (x2 + cx) * 65536;
  752.         dest_y = (y2 + cy) * 65536;
  753.  
  754.     /* Store src coordinate in explosion table's current position field */
  755.         pt_table [i].x.s.ms = (int) (src_x >> 16);
  756.         pt_table [i].x.s.ls = 0;
  757.         pt_table [i].y.s.ms = (int) (src_y >> 16);
  758.         pt_table [i].y.s.ls = 0;
  759.  
  760.         /* 
  761.      * Calculate velocity and acceleration.
  762.          *   accel = 2 * distance / #steps^2   (#steps is equivalent to time)
  763.          *   vel = accel * #steps 
  764.          */
  765.         accel = (2 * (dest_x - src_x))  /  ((long) e->nframes*e->nframes);
  766.         vel = (2 * (dest_x - src_x))  /  (long) e->nframes;
  767.         pt_table [i].xa.l = -accel + (e->wind * 100);
  768.         pt_table [i].xv.l = vel;
  769.  
  770.         accel = (2 * (dest_y - src_y))  /  ((long) e->nframes*e->nframes);
  771.         vel = (2 * (dest_y - src_y))  /  (long) e->nframes; 
  772.         pt_table [i].ya.l = -accel + (e->gravity * 100);
  773.         pt_table [i].yv.l = vel;
  774.  
  775.         pt_table [i].alive = 1;
  776.     }
  777.  
  778.     /* Generate the frame table for the explosion */
  779.     FrameInit (e, pt_table);
  780.  
  781.     /* Update stats used for calculating # of simultaneous explosions */
  782.     Total_frames += e->nframes;
  783.  
  784.     return (1);
  785. }
  786.  
  787.  
  788.  
  789. /****************************** Main *******************************/
  790.  
  791.  
  792. /*=====================================================================
  793.  * Given a keyboard key, return an explosion x-y starting point 
  794.  * roughly equivalent to the key's location on the keyboard.
  795.  *=====================================================================
  796.  */
  797. KeyToScreenPos (key, xsize, ysize, xpos, ypos)
  798. int key;    /* in: key's ascii value */
  799. int xsize;    /* in: x size of screen */
  800. int ysize;    /* in: y size of screen */
  801. int *xpos;    /* out: screen x position */
  802. int *ypos;    /* out: screen y position */
  803. {
  804.     int x, y;
  805.     static unsigned char key_xpos [] =
  806.     {
  807.     /*spc*/ 8, /*!*/ 1, /*"*/ 11, /*#*/ 3, /*$*/ 4, /*%*/ 5, /*&*/ 6,
  808.     /*'*/ 11, /*(*/ 9, /*)*/ 10, /***/ 8, /*+*/ 12, /*,*/ 8, 
  809.     /*-*/ 11, /*.*/ 9, /*slash*/ 10, /*0*/ 10, /*1*/ 1, /*2*/ 2,
  810.     /*3*/ 3, /*4*/ 4, /*5*/ 5, /*6*/ 6, /*7*/ 7, /*8*/ 8, /*9*/ 9,
  811.     /*:*/ 10, /*;*/ 10, /*<*/ 8, /*=*/ 12, /*>*/ 9, /*?*/ 10,
  812.     /*@*/ 2, /*A*/ 1, /*B*/ 5, /*C*/ 3, /*D*/ 3, /*E*/ 3, /*F*/ 4,
  813.     /*G*/ 5, /*H*/ 6, /*I*/ 8, /*J*/ 7, /*K*/ 8, /*L*/ 9, /*M*/ 7,
  814.     /*N*/ 6, /*O*/ 9, /*P*/ 10, /*Q*/ 1, /*R*/ 4, /*S*/ 2, /*T*/ 5,
  815.     /*U*/ 7, /*V*/ 4, /*W*/ 2, /*X*/ 2, /*Y*/ 6, /*Z*/ 1, /*[*/ 11,
  816.     /*\*/ 11, /*]*/12, /*^*/ 6, /*_*/ 10, /*`*/ 0
  817.     };
  818.     static unsigned char key_ypos [] =
  819.     {
  820.     /*spc*/ 5, /*!*/ 1, /*"*/ 3, /*#*/ 1, /*$*/ 1, /*%*/ 1, /*&*/ 1,
  821.     /*'*/ 3, /*(*/ 1, /*)*/ 1, /***/ 1, /*+*/ 1, /*,*/ 4, 
  822.     /*-*/ 1, /*.*/ 4, /*slash*/ 4, /*0*/ 1, /*1*/ 1, /*2*/ 1,
  823.     /*3*/ 1, /*4*/ 1, /*5*/ 1, /*6*/ 1, /*7*/ 1, /*8*/ 1, /*9*/ 1,
  824.     /*:*/ 3, /*;*/ 3, /*<*/ 4, /*=*/ 1, /*>*/ 4, /*?*/ 4,
  825.     /*@*/ 1, /*A*/ 3, /*B*/ 4, /*C*/ 4, /*D*/ 3, /*E*/ 2, /*F*/ 3,
  826.     /*G*/ 3, /*H*/ 3, /*I*/ 2, /*J*/ 3, /*K*/ 3, /*L*/ 3, /*M*/ 4,
  827.     /*N*/ 4, /*O*/ 2, /*P*/ 20, /*Q*/ 2, /*R*/ 2, /*S*/ 3, /*T*/ 2,
  828.     /*U*/ 2, /*V*/ 4, /*W*/ 2, /*X*/ 4, /*Y*/ 2, /*Z*/ 4, /*[*/ 2,
  829.     /*\*/ 5, /*]*/ 2, /*^*/ 1, /*_*/ 1, /*`*/ 1
  830.     };
  831. #   define MAX_KEYX 13
  832. #   define MAX_KEYY 5
  833.  
  834.     /* Special case: if space bar then make it random */
  835.     if (key == 32)
  836.     {
  837.     *xpos = rnd (xsize);
  838.     *ypos = rnd (ysize);
  839.     return;
  840.     }
  841.  
  842.     if (key > 96) key -= 32;
  843.     if (key > 96) key &= 63;
  844.     key -= 32;
  845.     if (key < 0) key =- key;
  846.  
  847.     x = key_xpos [key];
  848.     if (x >= MAX_KEYX) 
  849.     x = MAX_KEYX - 1;
  850.     x = (long) x * xsize / MAX_KEYX;
  851.     *xpos = x;
  852.  
  853.     y = key_ypos [key];
  854.     if (y >= MAX_KEYY) 
  855.     y = MAX_KEYY - 1;
  856.     y = (long) y * ysize / MAX_KEYY;
  857.     *ypos = y;
  858. }
  859.  
  860. /*=====================================================================
  861.  * Main - loop to check input and animate explosion events
  862.  *=====================================================================
  863.  */
  864. main (argc, argv)
  865.     int argc;
  866.     char *argv[];
  867. {
  868.     int key;
  869.     int start_count;
  870.     int start_interval;
  871.     int avg_num_frames;
  872.     int event_count = 0;
  873.     int last_event_frame_count = 0;
  874.     int i, j;
  875.     int xpos, ypos;
  876.  
  877.     /* Initialize the local memory allocator */
  878.     if (!MemInit())        
  879.     exit (1);
  880.  
  881.     /* Read in the command line arguments and explosion files */
  882.     Num_exp_desc = 0;
  883.     if (!GetArgs (argc, argv))
  884.     goto exit;
  885.     if (Num_exp_desc == 0) 
  886.     {
  887.     PUT ("\nERROR: no data files specified or all files are invalid.\n");
  888.     goto exit;
  889.     }
  890.  
  891.     /* Calculate the number of frames to wait between starting explosions */
  892.     avg_num_frames = Total_frames / Num_exp_desc;
  893.     if (Simul_exps > 0)
  894.     {
  895.         start_interval = avg_num_frames / Simul_exps;
  896.     start_count = start_interval;
  897.     }
  898.  
  899.     InitExpEvents();
  900.     gr_gmode();        /* enter graphics mode */
  901.  
  902.     /*
  903.      * Main loop: animate one explosion frame once each loop
  904.      * Exit loop when ESC pressed or when Event_limit explosions are done.
  905.      */
  906.     while ((key = ChkKey()) != 27)
  907.     {
  908.         /* 
  909.      * Start a new explosion event every start_interval loops or
  910.      * when a key is pressed.
  911.      */
  912.     if ((Simul_exps != 0  &&  start_count++ == start_interval)  ||  key)
  913.         {
  914.         /* Start only if event limit hasn't been reached */
  915.         if (Event_limit == 0  ||  event_count++ < Event_limit) 
  916.         {
  917.         if (key)
  918.             KeyToScreenPos (key, Screen_xsize, Screen_ysize, 
  919.                     &xpos, &ypos);
  920.         else
  921.         {
  922.             xpos = Screen_xsize/7 + rnd (Screen_xsize*5/7);
  923.             ypos = Screen_ysize/7 + rnd (Screen_ysize*5/7);
  924.         }
  925.                 StartExpEvent (xpos, ypos);
  926.                 start_count = 0;
  927.         }
  928.         }
  929.  
  930.         /* Play back one frame of each explosion event */
  931.         PlayExpEvents ();
  932.  
  933.     /* Exit if the event limit has been reached and the last explosion
  934.        has ended. */
  935.     if (Event_limit  &&  event_count >= Event_limit  &&
  936.         ++last_event_frame_count > avg_num_frames)
  937.         break;
  938.  
  939.         /* 
  940.          * Optional delay for machines that are too fast.
  941.          * The function call in the inner loop prevents optimizing
  942.          * compilers from optimizing away the loop.
  943.          */
  944.         if (Delay_factor > 0)
  945.             for (i=0; i < Delay_factor; i++) 
  946.                 for (j=0; j<100; j++)
  947.                     DummyDelay (i*j);     
  948.     }
  949.     gr_tmode ();
  950.  
  951. exit:
  952.     MemCleanup();    /* Release memory used by the local mem allocator */
  953. }
  954.  
  955. /* 
  956.  * Dummy function used for delays
  957.  */
  958. DummyDelay (i)
  959. int i;
  960. {
  961.     return (i * i);
  962. }
  963.  
  964.  
  965.