File:  [OS/2 SDKs] / os2sdk / demos / apps / chaser / chaser.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 12:25:59 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: os2sdk-1988, HEAD
Microsoft OS/2 SDK 03-01-1988

/*  SWARM
 *  Created by Microsoft Corp. 1986
 *
 *  the idea behind this game is as follows:
 *
 *   You have a collection of objects in the center of the playing field
 *   that you are trying to protect (just one object in current version). You
 *   control your own movements with the mouse. A number of "chasers" start
 *   around the edges of the field and begin moving towards the objects
 *   you want to protect. If you move the mouse on top of a chaser and click
 *   the left button, the chaser will be killed and disappear from the screen.
 *   But as you close in on the chaser, it will detect your presence and try
 *   to dodge you. Meanwhile the other chasers will continue to go after
 *   your objects. If one of the chasers reaches an object, it will begin
 *   dragging it away to the edge of the screen (currently the game just
 *   ends when the single object is reached). When all objects are dragged
 *   away, the game ends. If a chaser is killed while dragging an object, the
 *   object is left where it is and must be protected in place - player cannot
 *   move objects. If you kill all the chasers, a new group of faster ones
 *   will be spawned (currently the speed is constant). Your score is how
 *   many chasers you can kill (no score currently kept), so there is no
 *   advantage in sitting on the object for long periods.
 *
 * Swarm demonstrates several capabilities of OS/2 and the philosphy behind
 * them.  This program is made of three components: Initialization, the
 * mouse driven thread and the attacker thread.  The attacker thread is
 * launched as many times as there are attackers in a game.  Launching
 * the attacker several times takes full advantage of the OS to schedule
 * resources.  The programmer can think of the problem as only one attacker.
 * The system handles multiple instances of the thread.
 *
 * As the main loop launches threads it puts an ID code into the thread's
 * stack.  The code is used to index into the universe data.
 *
 * A ram semaphore is used to control access to global data.
 *
 * This demonstration shows the use of the following OS/2 system calls:
 *
 * Tasking:	       VIO API: 	     Mouse API:
 *
 *   DosSemRequest()	 VioScrollUp()	       MouOpen()
 *   DosSemClear()	 VioWrtCellStr()       MouSetPtrPos()
 *   DosCreateThread()	 VioSetCurType()       MouReadEventQue()
 *   DosExit()		 VioSetMode()
 *   DosSleep()
 */
#include <os2def.h>
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#include <bsedos.h>
#define INCL_SUB
#include <bsesub.h>
#include <malloc.h>
#undef NULL
#include <stdio.h>

#define  STACKSIZE  200

#define  DANGERZONE  3

#define  LONGNAP     500L
#define  SHORTNAP    150L

#define  WAIT (-1L)			/* Wait for ram Semaphore */

#define  CHASER    8			/* Number of chasers */

#define  SCREEN_HEIGHT	   24		/* Default screen size */
#define  SCREEN_WIDTH	   79

#define  GOAL univ[CHASER]		/* Macros for constant stuff */
#define  ME univ[ID]
#define  MOUSE univ[CHASER+1]

#define  ALIVE 1			/* Flags for attackers/goal */
#define  DEAD 0

char   Chaser[2] = { 0xE8, 0x20 };   /* character and attribute */
char	Prize[2] = { 0x03, 0x2C };   /* for our various objects */
char	Blank[2] = { 0x20, 0x22 };
char	Blood[2] = { 0x20, 0x44 };

struct {			      /* Universe structure and array */
    int     row;			/* univ[0] = chaser     */
    int     col;			/* univ[n-1] = chaser     */
    int     state;			/* univ[n] = GOAL */
} univ[CHASER+1];			/* univ[n+1]= MOUSE */

short		ScreenHeight,		/* Screen attributes */
		ScreenWidth;

HMOU		Mouse;			/* place for mouse handle */
ULONG		Shortnap;		/* Sleep times for chasers */
ULONG		Longnap;
ULONG		Semaphore = 0;		/* Ram semaphore */

struct _VIOCURSORINFO	NewCur; 	/* struct for setting cursor type */
struct _VIOCURSORINFO	OldCur;

struct _VIOMODEINFO	modedata;	/* Data saves for VIO mode */
struct _VIOMODEINFO	OldVioMode;

/*
 * Define all procedures before main.
 */
void Defender();
void CleanUp();
int InitGame();
void chaserthread();
int ParseCmdLine(int,char **);

/*
 * main(ac,av)
 *
 * Top level procedure and MOUSE thread for the GAME demo.
 */
int main(ac, av)
int ac;
char *av[];
{
    /*
     * Parse the command line and perform some initialization.
     */
    if (ParseCmdLine(ac,av)) {
	printf("usage: %s [24|43] [F|M|S]\n",av[0]);
	DosExit(EXIT_THREAD,1);
    }
    if (InitGame())		/* Init game, exit if some problem */
	DosExit(EXIT_PROCESS,1);

    Defender(); 		/* Run mouse loop (defend against the swarm) */

    CleanUp();
}

/*
 * Defender()
 *
 * This is the main loop of the mouse control thread.
 *
 * The semaphore is used to prevent the other threads from time slicing 
 * while this routine is examining and/or modifying the universe.  The 
 * Semaphore is grabbed after the read of the Mouse queue so we don't tie
 * up the attackers while waiting for a mouse event.
 */
void Defender()
{
    USHORT ReadType = 1,	/* Wait for mouse events */
	   alive,
	   i;
    struct _MOUEVENTINFO  MouInfo;    /* mouse event packet structure */

    alive = CHASER;

    do {
	MouReadEventQue( &MouInfo, &ReadType, Mouse); /* read where mouse is */

	DosSemRequest( &Semaphore, WAIT);

	if( MouInfo.fs & 1) {		      /* If the mouse has moved */
	    MOUSE.row = MouInfo.row;
	    MOUSE.col = MouInfo.col;
	}
	if( MouInfo.fs & 4 ) {		       /* if left button pressed, */
	    for (i = 0; i < CHASER; i++ ) {
		if( ( MOUSE.row == univ[i].row ) &&
		    ( MOUSE.col == univ[i].col ) &&  /* see if we hit one */
		    ( univ[i].state == ALIVE) ) {
		     univ[i].state = DEAD;	

		     DosBeep(300,75);		     /* make a dying sound */
		     DosBeep(600,75);
		     DosBeep(300,85);

		     alive--;		    /* Decrease number alive */
		     break;		    /* Can only kill one at a time */
		}
	    }
	}
	if( MouInfo.fs & 16 )	      /* If right button pressed... */
	    break;			/* End game, clean up */

	DosSemClear(&Semaphore);
    }
    while (GOAL.state == ALIVE && alive);    /* loop till all are dead */
}

/*
 * This thread manages the individual attackers.  It is spun off as
 * many times as needed for a game.
 *
 * The interaction of the mouse cursor and the chaser character is sort
 * of funny, hence the funny code, below.  The mouse cursor seems to
 * remember what was under it when it was written.  Hence we cannot erase
 * the chaser if the mouse is "sitting" on it.	If we do, then when the
 * mouse moves it will re-write the original object.  This shows up as
 * phantom chasers.
 */
void far chasethread(ID)	       /* code that controls each "chaser" */
int ID;
{
    short  row, col;	       /* Our current position */
    short  deltaX, deltaY;     /* how far from the mouse are we? */
    short  danger;	       /* flag to indicate not far enough! */
    short  m;		       /* general purpose indexes */


    /* Print out the initial chaser character */

    VioWrtCellStr( Chaser, 2, ME.row, ME.col, 0 );

    /*
     * Keep running as long as the goal and myself haven't been killed.
     */
    for (;;) {

	row = ME.row;		  /* Grab the current position */
	col = ME.col;
	/*
	 * If mouse is sitting upon the chaser, do nothing.  Allow
	 * the player some time to kill the chaser
	 */
	if ((MOUSE.row == row) && (MOUSE.col == col)) {
	    DosSleep( 1L );
	    continue;
	}
	DosSemRequest(&Semaphore, WAIT);
	/*
	 * If either the GOAL or Myself is dead, exit loop and clean up.
	 * This wasn't tested in the for loop since we don't want to exit
	 * if the MOUSE is sitting on the chaser.
	 */
	if (ME.state != ALIVE || GOAL.state != ALIVE)
	    break;

	deltaX = MOUSE.col - col;	/* calculate how far we are */
	deltaY = MOUSE.row - row;

	if (((deltaX < -DANGERZONE) || (DANGERZONE < deltaX)) ||
	    ((deltaY < -DANGERZONE) || (DANGERZONE < deltaY))) {

	    danger = 0;

	    if(GOAL.row < row)		    /* Creep towards the GOAL */
		row--;
	    else if (GOAL.row > row)
		row++;
	    if(GOAL.col < col)
		col--;
	    else if(GOAL.col > col)
		col++;
	}
	else {
	    danger = 1; 		    /* Run away from the mouse */

	    if ((MOUSE.row > row) && (row > 0))
		row--;
	    else if ((MOUSE.row < row) && (row < ScreenHeight))
		row++;
	    if ((MOUSE.col > col) && (col < ScreenWidth))
		col--;
	    else if ((MOUSE.col < col) && (col > 0))
		col++;
	}
	/*
	 * A quick and Dirty hack to prevent chasers from merging
	 */
	for (m = 0; m < CHASER; m++ ) {
	    if (univ[m].state == ALIVE &&
		univ[m].row == row &&
		univ[m].col == col &&
		m != ID) {
	       row += 1;
	       col += 3;
	    }
	}
	/*
	 * Zap the old chaser and print the new.  Release the semaphore
	 * after this, there can be no undesirable interactions now.
	 */
	VioWrtCellStr( Blank, 2, ME.row, ME.col, 0 );
	VioWrtCellStr( Chaser, 2, row, col, 0 );

	DosSemClear(&Semaphore);
	/*
	 * Update the current location
	 */
	ME.row = row;
	ME.col = col;
	/*
	 * See if we have reached the GOAL, if so eat it and exit
	 */
	if ((row == GOAL.row) && (col == GOAL.col)) {
	    VioWrtCellStr( Blank, 2, row, col, 0 );
	    DosBeep(600,175);
	    DosBeep(1200,175);	    /* if we reach the prize, let out a yell */
	    DosBeep(600,185);	    /* paint the screen red and end the game */
	    DosBeep(1200,175);
	    VioScrollUp( 0, 0, -1, -1, -1, Blood, 0 );
	    GOAL.state = DEAD;
	}
	/*
	 * Sleep an amount of time that varies depending
	 * upon the danger level
	 */
	if( danger )
	    DosSleep(Shortnap);
	else
	    DosSleep(Longnap);

    }
    /*
     * chaser is now dead or the game is over.
     * Erase its body and terminate the thread.  Release the semaphore.
     */
    DosSemClear(&Semaphore);

    if (GOAL.state == ALIVE) {
	VioWrtCellStr(Blank, 2, ME.row, ME.col, 0 );
    }
    DosExit( EXIT_THREAD ,0);
}

/*
 * InitGame()
 *
 * Initialize the GOAL, MOUSE and the CHASERS, launch each chase thread.
 *
 * Returns an error if any internal processing errors
 */
int InitGame()
{
    struct _PTRLOC InitMouPos;
    void far chasethread();		/* code to control chasers */
    PBYTE Tstack;			/* stack for new threads */
    unsigned chaseID;
    int i, rc;
    /*
     * Clear the screen.
     */
    VioScrollUp( 0, 0, -1, -1, -1, Blank, 0 );
    /*
     * Draw the prize
     */
    GOAL.row = ScreenHeight/2;
    GOAL.col = ScreenWidth /2;
    GOAL.state = ALIVE;
    VioWrtCellStr(Prize, 2, GOAL.row, GOAL.col, 0 );
    /*
     * Open the mouse pointer device and set it's location.
     */
    MouOpen( 0L, &Mouse );
    InitMouPos.row = GOAL.row;
    InitMouPos.col = GOAL.col;
    MouSetPtrPos( &InitMouPos, Mouse);
    MouDrawPtr(Mouse);
    /*
     * A simple minded initialization for the start of each chaser.
     * Some sort of random placement (based upon system time?) would
     * be nice.
     */
    univ[0].row = 0;  univ[0].col = 0;
    univ[1].row = 0;  univ[1].col = 25;
    univ[2].row = 0;  univ[2].col = 55;
    univ[3].row = 0;  univ[3].col = 79;
    univ[4].row = ScreenHeight;  univ[4].col = 0;
    univ[5].row = ScreenHeight;  univ[5].col = 25;
    univ[6].row = ScreenHeight;  univ[6].col = 55;
    univ[7].row = ScreenHeight;  univ[7].col = 79;
    /* 
     * Grab the semaphore to prevent chaser from running until we are done.
     */
    DosSemRequest(&Semaphore, WAIT);

    for( i = 0; i < CHASER; i++ ) {		/* for each of our threads... */
	univ[i].state = ALIVE;			/* Set each one alive */
	Tstack = (PBYTE)malloc(sizeof(int) * STACKSIZE);
	if (Tstack == NULL ) {			/* Create a stack */
	    printf( "thread %d stack malloc failed\n", i );
	    return(1);
	}
	Tstack += sizeof(int)*STACKSIZE; /* set stack pointer to correct end */
	*--Tstack = HIBYTE(i);
	*--Tstack = LOBYTE(i);		 /* Push the ID on as a parameter */

	rc = DosCreateThread(chasethread, &chaseID, Tstack);
	if(rc) {
	    printf( "create of thread %d failed, error: %d\n", i, rc );
	    return (1);
	}
    }
    DosSemClear(&Semaphore);

    return (0);
}

/*
 * CleanUp()
 *
 * Routine to reset the Video modes back to where they were.
 * (As best as possible).
 */
void CleanUp()
{
    char blank[2];

    DosSleep(1L);	     /* Yield the machine so attacker can clean up */
    VioSetMode( &OldVioMode, 0);
/*
    blank[0] = ' ';
    blank[1] = OldVioMode.color;
    VioScrollUp( 0, 0, -1, -1, -1, blank, 0 );
*/
    VioSetCurType( &OldCur, 0);
    DosExit(EXIT_PROCESS,0);	      /* Exit and terminate all threads. */
}

/*
 * ParseCmdLine(ac, av)
 *
 * Parses the command line arguments and sets up the game accordingly
 *
 */
int ParseCmdLine(ac,av)
int ac;
char **av;
{
    struct _VIOMODEINFO modedata;
    int    VioMode;

    Longnap = LONGNAP;
    Shortnap = SHORTNAP;
    ScreenWidth = SCREEN_WIDTH;
    ScreenHeight = SCREEN_HEIGHT;
    VioMode = 25;

    while(--ac) {
	av++;
	switch(**av) {
	    case 'f':
	    case 'F':
		Longnap = LONGNAP / 2;
		Shortnap= SHORTNAP/ 2;
		break;
	    case 'm':
	    case 'M':
		Longnap = LONGNAP;
		Shortnap= SHORTNAP;
		break;
	    case 's':
	    case 'S':
		Longnap = LONGNAP * 2;
		Shortnap= SHORTNAP* 2;
		break;
	    case '4':	    /* Assume 43 line mode was wanted */
		ScreenHeight = 42;
		ScreenWidth  = 79;
		VioMode = 43;
		break;
	    case '2':
		ScreenHeight = 24;
		ScreenWidth  = 79;
		VioMode = 25;
		break;
	    default:
		return(1);
	}
    }

    VioGetCurType(&OldCur, 0);		/* Save old cursor */

    modedata.cb = sizeof(modedata); /* change mode as needed */
    VioGetMode( &modedata, 0);
    OldVioMode = modedata;
    modedata.row = VioMode;
    VioSetMode( &modedata, 0);

    NewCur.yStart = 0;
    NewCur.cEnd = 0;
    NewCur.cx = 1;
    NewCur.attr = -1;

    VioSetCurType( &NewCur, 0 );	 /* make cursor go away */

    return (0);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.