|
|
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);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.