[comp.sources.games] v08i072: ttyexp - a fireworks program for ttys, Part01/01

billr@saab.CNA.TEK.COM (Bill Randle) (12/07/89)

Submitted-by: Dennis Lo <dlo%idacom.cs.ubc.cdn@relay.cdnnet.ca>
Posting-number: Volume 8, Issue 72
Archive-name: ttyexp/Part01

	[I tried this on my Sun 3/60 (OS3.5) and on a Vax (4.3bsd)
	 and it works (uncomment the #define for the Vax). -br]

[[From the author...
     Here's a submission to fill in the void in comp.sources.games.
It is an explosion-screen-clearer that is actually a much more general
fireworks program with many optional parameters.  Unfortunately, fireworks
more elaborate than a single explosion will require a fast, large screen
(eg. large text windows on a workstation) to be comprehensible.
     For 80x24 terminals at 9600 baud, the program will serve nicely as an 
interesting screen clearer, especially if it is set up to run on a press
of a function key.]]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  README makefile ttyexp.c
# Wrapped by billr@saab on Wed Dec  6 09:00:10 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(570 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
X     Here's a submission to fill in the void in comp.sources.games.
XIt is an explosion-screen-clearer that is actually a much more general
Xfireworks program with many optional parameters.  Unfortunately, fireworks
Xmore elaborate than a single explosion will require a fast, large screen
X(eg. large text windows on a workstation) to be comprehensible.
X
X     For 80x24 terminals at 9600 baud, the program will serve nicely as an 
Xinteresting screen clearer, especially if it is set up to run on a press
Xof a function key.
X
XDennis Lo
Xdlo%idacom.cs.ubc.cdn@relay.cdnnet.ca
END_OF_FILE
if test 570 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile'\"
else
echo shar: Extracting \"'makefile'\" \(85 characters\)
sed "s/^X//" >'makefile' <<'END_OF_FILE'
X#
X# Makefile for ttyexp
X#
Xttyexp: ttyexp.c
X	cc ttyexp.c -lcurses -ltermcap -o ttyexp
END_OF_FILE
if test 85 -ne `wc -c <'makefile'`; then
    echo shar: \"'makefile'\" unpacked with wrong size!
fi
# end of 'makefile'
fi
if test -f 'ttyexp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ttyexp.c'\"
else
echo shar: Extracting \"'ttyexp.c'\" \(19482 characters\)
sed "s/^X//" >'ttyexp.c' <<'END_OF_FILE'
X/* ttyexp.c - Explosion screen clearer for tty terminals 
X *
X * Compiling:
X * 	Use the command "cc ttyexp.c -lcurses -ltermcap -o ttyexp"
X *	I have only tested this on Sun 3's under SunOS 4.0, but I think it 
X *	should work under other flavours of Unix that support curses. 
X *
X * Running:
X *	Running "ttyexp" without any parameters will produce one explosion
X *	optimized for a 9600 baud terminal.  Command line switches can
X *	be given to set the number of simultaneous explosions, the explosion
X *	size, the number of points per explosion, etc.  "ttyexp -h" will 
X *	print instructions. 
X *
X * Bugs/Ugliness:
X *	This was hacked out of the IBM-PC fireworks program EXPLOD 1.1, 
X *	so don't be surprised to see lots of useless pc-isms in the code.
X *
X * Versions for other machines:
X *	The latest PC version is in /uploads/explod12.lzh at the ftp site 
X *	grape.ecs.clarkson.edu (128.153.13.196).  An older version (V1.1) that
X *	doesn't support EGA should be at comp.binaries.ibm.pc archive sites.
X *	Email me if you're interested in a version for the Sun 3/50 console 
X *	it accesses the screen via PixRect so it's not that fast).
X *
X * Author
X * 	Dennis Lo  89/10/10
X *	dlo@idacom.cs.ubc.ca  or  ...!alberta!ubc-cs!ubc-idacom!dlo
X *	4516 Albert St., Burnaby, B.C., Canada  V5C 2G5
X *
X *	This program is in the public domain.
X */
X#include <stdio.h>
X#include <ctype.h>
X#include <curses.h>
X#include <signal.h>
X
X/*#define LSWFIRST	/* define this for machines that are LS word first (e.g. Vax) */
X#define DEADPOINT 32767
X
X/*=====================================================================
X * Configuration Parameters
X *=====================================================================
X */
X/* video parameters */
Xstatic int Interlace_factor;
Xstatic int Screen_Xsize;
Xstatic int Screen_Ysize;
X
X/* explosion parameters */
X#define NUM_POINTS 300
X#define NUM_FRAMES 10
Xstatic int Num_points;
Xstatic int Xsize;
Xstatic int Ysize;
Xstatic int Gravity;
Xstatic int Wind = 0;
Xstatic int Centre_x;
Xstatic int Centre_y;
Xstatic int Centre_y_mask;
Xstatic char Exp_char = '@';
X
X/* Animation parameters */
Xstatic int Simul_exps = 0;      /* # of simultaneous explosions */
Xstatic int Trail_length = 4;    /* # of frames for erase to trail draw by */
Xstatic int Delay_factor = 0;    /* amount to delay in each frame */
Xstatic int Duration = NUM_FRAMES;
X
X/*=====================================================================
X * Structure of an explosion point at explosion creation time.
X *=====================================================================
X */
Xtypedef union 		/* 16.16 fixed point type */
X{
X    long l;
X    struct 
X    {
X#ifdef LSWFIRST
X        unsigned short ls;	/* the order is Endian-dependent! */
X        short ms;
X#else
X        short ms;
X        unsigned short ls;	/* the order is Endian-dependent! */
X#endif
X    } s;
X} fixed;
X
Xtypedef struct 		/* structure of single explosion point */
X{
X    fixed x, y;                 /* location of point */
X    fixed xv, yv;               /* velocity of point */
X    fixed xa, ya;               /* acceleration of point */
X    int alive;                  /* liveness: 0=dead */
X} exp_pt_type;
X
X/* Explosion creation points table - keeps track of points for creating
X   the explosion frames */
Xexp_pt_type Exp_table [NUM_POINTS+1];
X
X
X/*=====================================================================
X * Structure of a point in an explosion frame at playback time.
X *=====================================================================
X */
X/*
X * Each frame consists of NUM_POINTS structures of
X *      offset (2 bytes)
X *      value (1 byte)
X */
X/* Explosion playback frames table */
X/*
Xstatic unsigned char frame_table [NUM_FRAMES][NUM_POINTS][3];
X*/
X
Xtypedef struct
X{
X    int x;
X    int y;
X} coord_type;
X
Xtypedef coord_type frame_type [NUM_POINTS];
X
Xframe_type frame_table [NUM_FRAMES];
X
X
X/*=====================================================================
X * Structure of an explosion event
X *=====================================================================
X */
X#define MAX_EXPS 50     /* max # of simultaneous explosion events */
X
Xtypedef struct 
X{
X    coord_type centre;         /* addr of centre of explosion */
X    int frame_num;      	/* frame #. Dead if -1 */
X} exp_event_type;
X
X/* Explosion events table */
Xstatic exp_event_type exp_ev_tab [MAX_EXPS];
X
X
X
X
X/*=====================================================================
X * Main - loop to check input and call explosion steps
X *=====================================================================
X */
Xmain (argc, argv)
X    int argc;
X    char *argv[];
X{
X    int step;
X    int frame_i;
X    int key;
X    int start_count = 0;
X    int start_interval;
X    int i, j;
X
X    start_interval = GetArgs (argc, argv);
X
X    /*puts ("\nSetting up, please wait...");*/
X    FrameTableInit (Num_points);
X    InitExpEvents();
X    gr_gmode();
X
X    /* while (ChkKey() != 27) */
X    Duration = start_interval + (Duration / NUM_FRAMES) * NUM_FRAMES 
X		+ Trail_length;
X    for (step=0; step<Duration; step++)
X    {
X        /* Start a new explosion event every start_interval loops */
X        if (++start_count > start_interval  &&  step < Duration-Trail_length)
X        {
X            CreateExplosion (Screen_Xsize/4 + rnd (Screen_Xsize*2/4),
X                             Screen_Ysize/4 + rnd (Screen_Ysize*2/4));
X            start_count = 0;
X        }
X
X        /* Animate one frame of each explosion */
X        Explosion ();
X
X        /* 
X         * Optional delay for machines that are too fast.
X         * The function call in the inner loop prevents optimizing
X         * compilers from optimizing away the loop.
X         */
X        if (Delay_factor > 0)
X            for (i=0; i < Delay_factor; i++) 
X                for (j=0; j<100; j++)
X                    DummyDelay (i*j);     
X    }
X    gr_tmode ();
X}
X
X/* 
X * Dummy function used for delays
X */
XDummyDelay (i)
Xint i;
X{
X    return (i * i);
X}
X
X/*=====================================================================
X * Process command line arguments.  Returns start_interval.
X * Also sets up explosion parameters according to video card type.
X *=====================================================================
X */
Xint 
XGetArgs (argc, argv)
X    int argc;
X    char *argv[];
X{
X    int video_type = gr_card();		/* guess video card */
X
X    /* Set defaults based on assumed video card type */
X    SetVideoParams (video_type);         
X
X    /* Print instructions if no command line parameters are given */
X/*
X    if (argc == 1)
X        Instructions();
X*/
X
X    /*
X     * Loop to parse each command line parameter
X     */
X    while (--argc > 0) 
X    {
X        if (**++argv == '-') 
X        {
X            switch ((*argv)[1])
X            {
X                case 'v':               /* -v: video card type (c, h, e, s, t)*/
X                    SetVideoParams ((*argv)[2]);
X                    break;
X                case 's':               /* -s: # of simultaneous explosions */
X                    Simul_exps = atoi ((*argv) + 2);
X                    break;
X                case 'g':               /* -g: gravity (vert accel) */
X                    Gravity = atoi ((*argv) + 2);
X                    break;
X                case 'w':               /* -w: wind (horiz accel) */
X                    Wind = atoi ((*argv) + 2);
X                    break;
X                case 'x':               /* -x: explosion X size */
X                    Xsize = atoi ((*argv) + 2);
X                    break;
X                case 'y':               /* -y: explosion Y size */ Ysize = atoi ((*argv) + 2);
X                    break;
X                case 'p':               /* -p: # of explosion points */
X                    Num_points = atoi ((*argv) + 2);
X                    break;
X                case 't':               /* -t: trail length */
X                    Trail_length = atoi ((*argv) + 2);
X                    break;
X                case 'd':               /* -d: delay factor */
X                    Delay_factor = atoi ((*argv) + 2);
X                    break;
X                case 'D':               /* -D: Duration */
X                    Duration = atoi ((*argv) + 2);
X                    break;
X		case 'c':		/* -c: character */
X		    if (isdigit ((*argv)[3]))
X			Exp_char = atoi ((*argv) + 2);
X		    else
X			Exp_char = (*argv)[2];
X		    break;
X		case 'h':
X		    Instructions();
X		    exit ();
X                default:
X                    printf ("*** Invalid option: %s\n", *argv);
X                    Instructions();
X                    exit ();
X            }
X        }
X    }
X    return (NUM_FRAMES / Simul_exps);
X}
X
X/*=====================================================================
X * Set video card-related parameters
X *=====================================================================
X */
XSetVideoParams (video_card)
X    char video_card;
X{
X    int i;
X    int *defarr;
X#   define NUM_CARDS 6
X    static char index_char[NUM_CARDS] = 
X	{ 'c', 'h', 'e', 'v', 's', 't' };
X    static int defaults [NUM_CARDS][9] = { 
X	2, 640, 200, 127, 50, 1500, 8, 120, 3,		/* cga */
X	4, 720, 348, 150, 90, 2000, 10, 160, 5,		/* hgc */
X	1, 640, 350, 150,  90, 2000, 10, 160, 5,	/* ega */
X	1, 640, 480, 150, 120, 2500, 12, 170, 5,	/* vga */
X	1, 1600,1280,300, 250, 4000, 1, 300, 8,		/* sun */
X	1, 80,  24,  40,  15,  1000, 1, 20, 1		/* tty */
X    };
X	
X    /* return defaults index for the given video_card */
X    for (i=0; i<NUM_CARDS; i++)
X        if (video_card == index_char[i])
X	    break;
X
X    defarr = defaults [i];
X    Interlace_factor = defarr[0];
X    Screen_Xsize = defarr[1];
X    Screen_Ysize = defarr[2];
X    if (Xsize == 0) Xsize = defarr[3];
X    if (Ysize == 0) Ysize = defarr[4];
X    if (Gravity == 0) Gravity = defarr[5];
X    if (Simul_exps == 0) Simul_exps = defarr[6];
X    if (Num_points == 0) Num_points = defarr[7];
X    if (Trail_length == 0) Trail_length = defarr[8];
X
X    Centre_x = (Screen_Xsize / 2);
X    Centre_y = (Screen_Ysize / 2);
X    Centre_y_mask = (0xffff - (Interlace_factor - 1));
X    gr_setcard (video_card);
X}
X
X/*=====================================================================
X * Print instructions
X *=====================================================================
X */
XInstructions ()
X{
X    puts ("Usage: ttyexp <parameters>");
X    puts ("Parameters can be one of");
X    puts ("   -s<n> :<n> = # of simultaneous explosions. Default: 1");
X    puts ("   -x<n> :<n> = Explosion X size.             Default: 40");
X    puts ("   -y<n> :<n> = Explosion Y size.             Default: 15");
X    puts ("   -p<n> :<n> = #pts/explosion. Max 300.      Default: 10");
X    puts ("   -t<n> :<n> = Trail length.                 Default: 4");
X    puts ("   -d<n> :<n> = Delay factor.                 Default: 0");
X    puts ("   -g<n> :<n> = Gravity (vert accel).         Default: 1000");
X    puts ("   -w<n> :<n> = Wind (horiz accel).           Default: 0");
X    puts ("   -c<n> :<n> = Explosion character.          Default: '@'");
X    puts ("   -D<n> :<n> = Duration.                     Default: 10");
X}
X
X/***************** Explosion event handling module *******************/
X/*=====================================================================
X * Perform 1 explosion step
X *=====================================================================
X */
XExplosion ()
X{
X    int i, j;
X
X    /*
X     * Loop to animate one frame for each active explosion in the
X     * explosion table.
X     */
X    for (i=0; i < MAX_EXPS; i++)
X    {
X        if (exp_ev_tab[i].frame_num != -1)
X        {
X            /* if finished last frame of this explosion event */
X            if (++exp_ev_tab[i].frame_num == NUM_FRAMES + Trail_length)
X            {
X                /* turn off final frame's points */
X                gr_frplot (Num_points,
X                        frame_table[exp_ev_tab[i].frame_num - Trail_length-1],
X                        &exp_ev_tab[i].centre, ' ');
X
X                /* free current event's entry in explosion events table */
X                exp_ev_tab[i].frame_num = -1;
X            }
X            else
X            {
X                /* Turn off previous frame's points (unless no prev frame) */
X                if (exp_ev_tab[i].frame_num-Trail_length-1 >= 0)
X                    gr_frplot (Num_points,
X                        frame_table[exp_ev_tab[i].frame_num - Trail_length-1],
X                        &exp_ev_tab[i].centre, ' ');
X
X                /* Turn on current frame's points */
X                if (exp_ev_tab[i].frame_num < NUM_FRAMES)
X                    gr_frplot (Num_points,
X                        frame_table[exp_ev_tab[i].frame_num],
X                        &exp_ev_tab[i].centre, Exp_char);
X            }
X        }
X    }
X}
X
X
X/*=====================================================================
X * Add an explosion to the events table 
X *=====================================================================
X */
XCreateExplosion (x, y)
X    int x, y;
X{
X    int i;
X
X    y &= Centre_y_mask;
X
X    /* Find the first free entry in the table */
X    for (i=0; i<MAX_EXPS; i++)
X        if (exp_ev_tab[i].frame_num == -1) 
X            break;
X
X    /* don't do anything if table is full */
X    if (i == MAX_EXPS) 
X        return;
X
X    exp_ev_tab[i].centre.x = x; 
X    exp_ev_tab[i].centre.y = y; 	
X    exp_ev_tab[i].frame_num = 0;
X
X    /* Turn on first frame's points */
X    gr_frplot (Num_points, frame_table[0], &exp_ev_tab[i].centre, Exp_char);
X}
X
X/* 
X * Initialize events table 
X */
XInitExpEvents ()
X{
X    int i;
X    for (i=0; i<MAX_EXPS; i++)
X        exp_ev_tab[i].frame_num = -1;
X}
X
X
X
X/*********************** Explosion Frames *****************************/
X/*====================================================================
X * Create an explosion, storing it in the explosion frames table.
X * Returns addr of explosion centre.
X *====================================================================
X */
XFrameTableInit (num_points)
X    int num_points;
X{
X    exp_pt_type *curr_pt;
X    int i;
X    int point_count;            /* total # of points processed */
X    int fade_window;            /* fade window counter (can fade if 0) */
X    int delay;
X    int frame_i;
X    int centre_x;
X    int centre_y;
X    short *s;
X
X    /*
X     * Initialize points and plot them at their initial positions
X     */
X    ExpInit (num_points, Exp_table, &centre_x, &centre_y);
X
X    /*
X     * Loop to move the explosion through NUM_FRAMES frames
X     */
X    point_count = 0;
X    fade_window = 0;
X    for (frame_i = 0; frame_i < NUM_FRAMES; frame_i++)
X    {
X        /*
X         * Loop to reset, move, and set every point in the table
X         */
X        for (i=0; i<num_points; i++)
X        {
X            curr_pt = Exp_table + i;
X
X            /* assume point is dead first */
X	    frame_table [frame_i][i].x = DEADPOINT;
X
X            /* Do the point only if it is alive */
X            if (curr_pt->alive)
X            {
X                /* calc next position */
X                curr_pt->x.l += curr_pt->xv.l;
X                curr_pt->y.l += curr_pt->yv.l;
X                curr_pt->xv.l += curr_pt->xa.l;
X                curr_pt->yv.l += curr_pt->ya.l;
X
X                /* fade out period count */
X                fade_window = (fade_window + 1) & 7;
X
X                /* Check if point should die */
X                if (curr_pt->x.s.ms >= Screen_Xsize  ||  curr_pt->x.s.ms < 0
X                || curr_pt->y.s.ms > Screen_Ysize  ||  curr_pt->y.s.ms < 0
X                || (++point_count > num_points * 30  &&  fade_window == 0))
X                    curr_pt->alive = 0;
X
X                else /* if not out */
X                {
X                    /* if not out then save point's coords wrt centre */
X		    frame_table [frame_i][i].x = curr_pt->x.s.ms - centre_x;
X		    frame_table [frame_i][i].y = curr_pt->y.s.ms - centre_y;
X                }
X            }
X        }
X    }
X}
X
X/*====================================================================
X * Set up the initial points for an explosion.
X * Returns addr of explosion centre.
X *====================================================================
X */
XExpInit (num_points, exp_table, centre_x, centre_y)
X    int num_points;
X    exp_pt_type exp_table[];
X    int *centre_x, *centre_y;
X{
X    long dest_x, dest_y;
X    long src_x, src_y;
X    long accel, vel;
X    int cx;
X    int cy;
X    int i = 0;
X
X    /* Clear explosion table */
X    memset ((char*) exp_table, 0, num_points * sizeof(exp_pt_type));
X
X    /* Calc explosion centre coordinates */
X    cx = Centre_x;
X    cy = Centre_y & Centre_y_mask;
X
X    for (i=0; i<num_points; i++) 
X    {
X        exp_table [i].alive = 1;
X
X        /* 
X         * Put in explosion centre as starting coordinate
X         */
X        src_x = ((long) cx) * 65536;
X        src_y = ((long) cy) * 65536;
X        exp_table [i].x.s.ms = (int) (src_x >> 16);
X        exp_table [i].x.s.ls = 0;
X        exp_table [i].y.s.ms = (int) (src_y >> 16);
X        exp_table [i].y.s.ls = 0;
X
X        /* 
X         * Randomly select a destination that is inside the ellipse with
X         * X and Y radii of (Xsize, Ysize).
X         */
X        do 
X        {
X            dest_x = rnd (2*Xsize) - Xsize;
X            dest_y = rnd (2*Ysize) - Ysize;
X        } while ((long) Ysize * Ysize * dest_x * dest_x + 
X                 (long) Xsize * Xsize * dest_y * dest_y
X                 > (long) Ysize * Ysize * Xsize * Xsize);
X
X        /* Convert to fixed pt. Can't use shifts because they are unsigned */
X        dest_x = (dest_x + cx) * 65536;
X        dest_y = (dest_y + cy) * 65536;
X
X        /* 
X         * accel = 2 * distance / #steps^2   (#steps is equivalent to time)
X         * vel = accel * #steps 
X         */
X        accel = (2 * (dest_x - src_x))  /  ((long) NUM_FRAMES*NUM_FRAMES);
X        vel = (2 * (dest_x - src_x))  /  (long) NUM_FRAMES;
X        exp_table [i].xa.l = -accel + Wind;
X        exp_table [i].xv.l = vel;
X
X        accel = (2 * (dest_y - src_y))  /  ((long) NUM_FRAMES*NUM_FRAMES);
X        vel = (2 * (dest_y - src_y))  /  (long) NUM_FRAMES; 
X        exp_table [i].ya.l = -accel + Gravity;
X        exp_table [i].yv.l = vel;
X    }
X    *centre_x = cx;
X    *centre_y = cy;
X}
X
X
X/*====================================================================
X * Return a random number between 1..maxval
X *====================================================================
X */
Xint
Xrnd (maxval)
X{
X#   define MAX_RAND 32767       /* max val returned by rand() */
X    long l;
X
X    l = (long) maxval * (rand()&MAX_RAND) / MAX_RAND;
X    return ((int) l);
X}
X
X
X
X
X/*=====================================================================
X * tty video routines - replaces the code in the PC version's expa.asm
X *=====================================================================
X */
Xint_handler()
X{
X    signal(SIGINT, SIG_IGN);
X    mvcur(0, COLS-1, LINES-1, 0);
X    endwin();
X    exit(0);
X}
Xgr_setcard()
X{
X}
Xgr_card()
X{
X    srand (getpid());
X    return ('t');
X}
Xgr_gmode()
X{
X    char *getenv();
X    char *sp;
X
X    initscr();
X    if (isatty(0)) 
X    {
X        gettmode();
X        if (sp=(char*)getenv("TERM")) setterm(sp);
X    }
X    else
X        setterm(Def_term);
X    signal (SIGINT, int_handler);
X    noecho();
X    nonl();
X    leaveok(stdscr, FALSE);
X    scrollok(stdscr, FALSE);
X}
X
Xgr_tmode()
X{
X    clear();
X    move (0, 0);
X    refresh();
X    endwin();
X    signal(SIGINT, SIG_IGN);
X}
X
Xgr_frplot(np, frame, centre, plotval)
Xint np;
Xcoord_type frame[];
Xcoord_type *centre;
Xchar plotval;
X{
X    int i, x, y;
X    for (i=0; i<Num_points; i++)
X    {
X	x = frame[i].x + centre->x;
X	y = frame[i].y + centre->y;
X	move (y,x);
X	addch (plotval);
X    }
X    refresh();
X}
X
END_OF_FILE
if test 19482 -ne `wc -c <'ttyexp.c'`; then
    echo shar: \"'ttyexp.c'\" unpacked with wrong size!
fi
# end of 'ttyexp.c'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0