home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk12 / chaser / chaser.c next >
Encoding:
C/C++ Source or Header  |  1989-11-20  |  13.8 KB  |  487 lines

  1. /*  SWARM
  2.  *  Created by Microsoft Corp. 1986
  3.  *
  4.  *  the idea behind this game is as follows:
  5.  *
  6.  *   You have a collection of objects in the center of the playing field
  7.  *   that you are trying to protect (just one object in current version). You
  8.  *   control your own movements with the mouse. A number of "chasers" start
  9.  *   around the edges of the field and begin moving towards the objects
  10.  *   you want to protect. If you move the mouse on top of a chaser and click
  11.  *   the left button, the chaser will be killed and disappear from the screen.
  12.  *   But as you close in on the chaser, it will detect your presence and try
  13.  *   to dodge you. Meanwhile the other chasers will continue to go after
  14.  *   your objects. If one of the chasers reaches an object, it will begin
  15.  *   dragging it away to the edge of the screen (currently the game just
  16.  *   ends when the single object is reached). When all objects are dragged
  17.  *   away, the game ends. If a chaser is killed while dragging an object, the
  18.  *   object is left where it is and must be protected in place - player cannot
  19.  *   move objects. If you kill all the chasers, a new group of faster ones
  20.  *   will be spawned (currently the speed is constant). Your score is how
  21.  *   many chasers you can kill (no score currently kept), so there is no
  22.  *   advantage in sitting on the object for long periods.
  23.  *
  24.  * Swarm demonstrates several capabilities of OS/2 and the philosphy behind
  25.  * them.  This program is made of three components: Initialization, the
  26.  * mouse driven thread and the attacker thread.  The attacker thread is
  27.  * launched as many times as there are attackers in a game.  Launching
  28.  * the attacker several times takes full advantage of the OS to schedule
  29.  * resources.  The programmer can think of the problem as only one attacker.
  30.  * The system handles multiple instances of the thread.
  31.  *
  32.  * As the main loop launches threads it puts an ID code into the thread's
  33.  * stack.  The code is used to index into the universe data.
  34.  *
  35.  * A ram semaphore is used to control access to global data.
  36.  *
  37.  * This demonstration shows the use of the following OS/2 system calls:
  38.  *
  39.  * Tasking:           VIO API:          Mouse API:
  40.  *
  41.  *   DosSemRequest()     VioScrollUp()           MouOpen()
  42.  *   DosSemClear()     VioWrtCellStr()       MouSetPtrPos()
  43.  *   DosCreateThread()     VioSetCurType()       MouReadEventQue()
  44.  *   DosExit()         VioSetMode()
  45.  *   DosSleep()
  46.  */
  47. #include <os2def.h>
  48. #define INCL_DOSPROCESS
  49. #define INCL_DOSSEMAPHORES
  50. #include <bsedos.h>
  51. #define INCL_SUB
  52. #include <bsesub.h>
  53. #include <malloc.h>
  54. #undef NULL
  55. #include <stdio.h>
  56.  
  57. #define  STACKSIZE  200
  58.  
  59. #define  DANGERZONE  3
  60.  
  61. #define  LONGNAP     500L
  62. #define  SHORTNAP    150L
  63.  
  64. #define  WAIT (-1L)            /* Wait for ram Semaphore */
  65.  
  66. #define  CHASER    8            /* Number of chasers */
  67.  
  68. #define  SCREEN_HEIGHT       24        /* Default screen size */
  69. #define  SCREEN_WIDTH       79
  70.  
  71. #define  GOAL univ[CHASER]        /* Macros for constant stuff */
  72. #define  ME univ[ID]
  73. #define  MOUSE univ[CHASER+1]
  74.  
  75. #define  ALIVE 1            /* Flags for attackers/goal */
  76. #define  DEAD 0
  77.  
  78. char   Chaser[2] = { 0xE8, 0x20 };   /* character and attribute */
  79. char    Prize[2] = { 0x03, 0x2C };   /* for our various objects */
  80. char    Blank[2] = { 0x20, 0x22 };
  81. char    Blood[2] = { 0x20, 0x44 };
  82.  
  83. struct {                  /* Universe structure and array */
  84.     int     row;            /* univ[0] = chaser     */
  85.     int     col;            /* univ[n-1] = chaser     */
  86.     int     state;            /* univ[n] = GOAL */
  87. } univ[CHASER+1];            /* univ[n+1]= MOUSE */
  88.  
  89. short        ScreenHeight,        /* Screen attributes */
  90.         ScreenWidth;
  91.  
  92. HMOU        Mouse;            /* place for mouse handle */
  93. ULONG        Shortnap;        /* Sleep times for chasers */
  94. ULONG        Longnap;
  95. ULONG        Semaphore = 0;        /* Ram semaphore */
  96.  
  97. struct _VIOCURSORINFO    NewCur;     /* struct for setting cursor type */
  98. struct _VIOCURSORINFO    OldCur;
  99.  
  100. struct _VIOMODEINFO    modedata;    /* Data saves for VIO mode */
  101. struct _VIOMODEINFO    OldVioMode;
  102.  
  103. /*
  104.  * Define all procedures before main.
  105.  */
  106. void Defender();
  107. void CleanUp();
  108. int InitGame();
  109. void chaserthread();
  110. int ParseCmdLine(int,char **);
  111.  
  112. /*
  113.  * main(ac,av)
  114.  *
  115.  * Top level procedure and MOUSE thread for the GAME demo.
  116.  */
  117. int main(ac, av)
  118. int ac;
  119. char *av[];
  120. {
  121.     /*
  122.      * Parse the command line and perform some initialization.
  123.      */
  124.     if (ParseCmdLine(ac,av)) {
  125.     printf("usage: %s [24|43] [F|M|S]\n",av[0]);
  126.     DosExit(EXIT_THREAD,1);
  127.     }
  128.     if (InitGame())        /* Init game, exit if some problem */
  129.     DosExit(EXIT_PROCESS,1);
  130.  
  131.     Defender();         /* Run mouse loop (defend against the swarm) */
  132.  
  133.     CleanUp();
  134. }
  135.  
  136. /*
  137.  * Defender()
  138.  *
  139.  * This is the main loop of the mouse control thread.
  140.  *
  141.  * The semaphore is used to prevent the other threads from time slicing 
  142.  * while this routine is examining and/or modifying the universe.  The 
  143.  * Semaphore is grabbed after the read of the Mouse queue so we don't tie
  144.  * up the attackers while waiting for a mouse event.
  145.  */
  146. void Defender()
  147. {
  148.     USHORT ReadType = 1,    /* Wait for mouse events */
  149.        alive,
  150.        i;
  151.     struct _MOUEVENTINFO  MouInfo;    /* mouse event packet structure */
  152.  
  153.     alive = CHASER;
  154.  
  155.     do {
  156.     MouReadEventQue( &MouInfo, &ReadType, Mouse); /* read where mouse is */
  157.  
  158.     DosSemRequest( &Semaphore, WAIT);
  159.  
  160.     if( MouInfo.fs & 1) {              /* If the mouse has moved */
  161.         MOUSE.row = MouInfo.row;
  162.         MOUSE.col = MouInfo.col;
  163.     }
  164.     if( MouInfo.fs & 4 ) {               /* if left button pressed, */
  165.         for (i = 0; i < CHASER; i++ ) {
  166.         if( ( MOUSE.row == univ[i].row ) &&
  167.             ( MOUSE.col == univ[i].col ) &&  /* see if we hit one */
  168.             ( univ[i].state == ALIVE) ) {
  169.              univ[i].state = DEAD;    
  170.  
  171.              DosBeep(300,75);             /* make a dying sound */
  172.              DosBeep(600,75);
  173.              DosBeep(300,85);
  174.  
  175.              alive--;            /* Decrease number alive */
  176.              break;            /* Can only kill one at a time */
  177.         }
  178.         }
  179.     }
  180.     if( MouInfo.fs & 16 )          /* If right button pressed... */
  181.         break;            /* End game, clean up */
  182.  
  183.     DosSemClear(&Semaphore);
  184.     }
  185.     while (GOAL.state == ALIVE && alive);    /* loop till all are dead */
  186. }
  187.  
  188. /*
  189.  * This thread manages the individual attackers.  It is spun off as
  190.  * many times as needed for a game.
  191.  *
  192.  * The interaction of the mouse cursor and the chaser character is sort
  193.  * of funny, hence the funny code, below.  The mouse cursor seems to
  194.  * remember what was under it when it was written.  Hence we cannot erase
  195.  * the chaser if the mouse is "sitting" on it.    If we do, then when the
  196.  * mouse moves it will re-write the original object.  This shows up as
  197.  * phantom chasers.
  198.  */
  199. void far chasethread(ID)           /* code that controls each "chaser" */
  200. int ID;
  201. {
  202.     short  row, col;           /* Our current position */
  203.     short  deltaX, deltaY;     /* how far from the mouse are we? */
  204.     short  danger;           /* flag to indicate not far enough! */
  205.     short  m;               /* general purpose indexes */
  206.  
  207.  
  208.     /* Print out the initial chaser character */
  209.  
  210.     VioWrtCellStr( Chaser, 2, ME.row, ME.col, 0 );
  211.  
  212.     /*
  213.      * Keep running as long as the goal and myself haven't been killed.
  214.      */
  215.     for (;;) {
  216.  
  217.     row = ME.row;          /* Grab the current position */
  218.     col = ME.col;
  219.     /*
  220.      * If mouse is sitting upon the chaser, do nothing.  Allow
  221.      * the player some time to kill the chaser
  222.      */
  223.     if ((MOUSE.row == row) && (MOUSE.col == col)) {
  224.         DosSleep( 1L );
  225.         continue;
  226.     }
  227.     DosSemRequest(&Semaphore, WAIT);
  228.     /*
  229.      * If either the GOAL or Myself is dead, exit loop and clean up.
  230.      * This wasn't tested in the for loop since we don't want to exit
  231.      * if the MOUSE is sitting on the chaser.
  232.      */
  233.     if (ME.state != ALIVE || GOAL.state != ALIVE)
  234.         break;
  235.  
  236.     deltaX = MOUSE.col - col;    /* calculate how far we are */
  237.     deltaY = MOUSE.row - row;
  238.  
  239.     if (((deltaX < -DANGERZONE) || (DANGERZONE < deltaX)) ||
  240.         ((deltaY < -DANGERZONE) || (DANGERZONE < deltaY))) {
  241.  
  242.         danger = 0;
  243.  
  244.         if(GOAL.row < row)            /* Creep towards the GOAL */
  245.         row--;
  246.         else if (GOAL.row > row)
  247.         row++;
  248.         if(GOAL.col < col)
  249.         col--;
  250.         else if(GOAL.col > col)
  251.         col++;
  252.     }
  253.     else {
  254.         danger = 1;             /* Run away from the mouse */
  255.  
  256.         if ((MOUSE.row > row) && (row > 0))
  257.         row--;
  258.         else if ((MOUSE.row < row) && (row < ScreenHeight))
  259.         row++;
  260.         if ((MOUSE.col > col) && (col < ScreenWidth))
  261.         col--;
  262.         else if ((MOUSE.col < col) && (col > 0))
  263.         col++;
  264.     }
  265.     /*
  266.      * A quick and Dirty hack to prevent chasers from merging
  267.      */
  268.     for (m = 0; m < CHASER; m++ ) {
  269.         if (univ[m].state == ALIVE &&
  270.         univ[m].row == row &&
  271.         univ[m].col == col &&
  272.         m != ID) {
  273.            row += 1;
  274.            col += 3;
  275.         }
  276.     }
  277.     /*
  278.      * Zap the old chaser and print the new.  Release the semaphore
  279.      * after this, there can be no undesirable interactions now.
  280.      */
  281.     VioWrtCellStr( Blank, 2, ME.row, ME.col, 0 );
  282.     VioWrtCellStr( Chaser, 2, row, col, 0 );
  283.  
  284.     DosSemClear(&Semaphore);
  285.     /*
  286.      * Update the current location
  287.      */
  288.     ME.row = row;
  289.     ME.col = col;
  290.     /*
  291.      * See if we have reached the GOAL, if so eat it and exit
  292.      */
  293.     if ((row == GOAL.row) && (col == GOAL.col)) {
  294.         VioWrtCellStr( Blank, 2, row, col, 0 );
  295.         DosBeep(600,175);
  296.         DosBeep(1200,175);        /* if we reach the prize, let out a yell */
  297.         DosBeep(600,185);        /* paint the screen red and end the game */
  298.         DosBeep(1200,175);
  299.         VioScrollUp( 0, 0, -1, -1, -1, Blood, 0 );
  300.         GOAL.state = DEAD;
  301.     }
  302.     /*
  303.      * Sleep an amount of time that varies depending
  304.      * upon the danger level
  305.      */
  306.     if( danger )
  307.         DosSleep(Shortnap);
  308.     else
  309.         DosSleep(Longnap);
  310.  
  311.     }
  312.     /*
  313.      * chaser is now dead or the game is over.
  314.      * Erase its body and terminate the thread.  Release the semaphore.
  315.      */
  316.     DosSemClear(&Semaphore);
  317.  
  318.     if (GOAL.state == ALIVE) {
  319.     VioWrtCellStr(Blank, 2, ME.row, ME.col, 0 );
  320.     }
  321.     DosExit( EXIT_THREAD ,0);
  322. }
  323.  
  324. /*
  325.  * InitGame()
  326.  *
  327.  * Initialize the GOAL, MOUSE and the CHASERS, launch each chase thread.
  328.  *
  329.  * Returns an error if any internal processing errors
  330.  */
  331. int InitGame()
  332. {
  333.     struct _PTRLOC InitMouPos;
  334.     void far chasethread();        /* code to control chasers */
  335.     PBYTE Tstack;            /* stack for new threads */
  336.     unsigned chaseID;
  337.     int i, rc;
  338.     /*
  339.      * Clear the screen.
  340.      */
  341.     VioScrollUp( 0, 0, -1, -1, -1, Blank, 0 );
  342.     /*
  343.      * Draw the prize
  344.      */
  345.     GOAL.row = ScreenHeight/2;
  346.     GOAL.col = ScreenWidth /2;
  347.     GOAL.state = ALIVE;
  348.     VioWrtCellStr(Prize, 2, GOAL.row, GOAL.col, 0 );
  349.     /*
  350.      * Open the mouse pointer device and set it's location.
  351.      */
  352.     MouOpen( 0L, &Mouse );
  353.     InitMouPos.row = GOAL.row;
  354.     InitMouPos.col = GOAL.col;
  355.     MouSetPtrPos( &InitMouPos, Mouse);
  356.     MouDrawPtr(Mouse);
  357.     /*
  358.      * A simple minded initialization for the start of each chaser.
  359.      * Some sort of random placement (based upon system time?) would
  360.      * be nice.
  361.      */
  362.     univ[0].row = 0;  univ[0].col = 0;
  363.     univ[1].row = 0;  univ[1].col = 25;
  364.     univ[2].row = 0;  univ[2].col = 55;
  365.     univ[3].row = 0;  univ[3].col = 79;
  366.     univ[4].row = ScreenHeight;  univ[4].col = 0;
  367.     univ[5].row = ScreenHeight;  univ[5].col = 25;
  368.     univ[6].row = ScreenHeight;  univ[6].col = 55;
  369.     univ[7].row = ScreenHeight;  univ[7].col = 79;
  370.     /* 
  371.      * Grab the semaphore to prevent chaser from running until we are done.
  372.      */
  373.     DosSemRequest(&Semaphore, WAIT);
  374.  
  375.     for( i = 0; i < CHASER; i++ ) {        /* for each of our threads... */
  376.     univ[i].state = ALIVE;            /* Set each one alive */
  377.     Tstack = (PBYTE)malloc(sizeof(int) * STACKSIZE);
  378.     if (Tstack == NULL ) {            /* Create a stack */
  379.         printf( "thread %d stack malloc failed\n", i );
  380.         return(1);
  381.     }
  382.     Tstack += sizeof(int)*STACKSIZE; /* set stack pointer to correct end */
  383.     *--Tstack = HIBYTE(i);
  384.     *--Tstack = LOBYTE(i);         /* Push the ID on as a parameter */
  385.  
  386.     rc = DosCreateThread(chasethread, &chaseID, Tstack);
  387.     if(rc) {
  388.         printf( "create of thread %d failed, error: %d\n", i, rc );
  389.         return (1);
  390.     }
  391.     }
  392.     DosSemClear(&Semaphore);
  393.  
  394.     return (0);
  395. }
  396.  
  397. /*
  398.  * CleanUp()
  399.  *
  400.  * Routine to reset the Video modes back to where they were.
  401.  * (As best as possible).
  402.  */
  403. void CleanUp()
  404. {
  405.     char blank[2];
  406.  
  407.     DosSleep(1L);         /* Yield the machine so attacker can clean up */
  408.     VioSetMode( &OldVioMode, 0);
  409. /*
  410.     blank[0] = ' ';
  411.     blank[1] = OldVioMode.color;
  412.     VioScrollUp( 0, 0, -1, -1, -1, blank, 0 );
  413. */
  414.     VioSetCurType( &OldCur, 0);
  415.     DosExit(EXIT_PROCESS,0);          /* Exit and terminate all threads. */
  416. }
  417.  
  418. /*
  419.  * ParseCmdLine(ac, av)
  420.  *
  421.  * Parses the command line arguments and sets up the game accordingly
  422.  *
  423.  */
  424. int ParseCmdLine(ac,av)
  425. int ac;
  426. char **av;
  427. {
  428.     struct _VIOMODEINFO modedata;
  429.     int    VioMode;
  430.  
  431.     Longnap = LONGNAP;
  432.     Shortnap = SHORTNAP;
  433.     ScreenWidth = SCREEN_WIDTH;
  434.     ScreenHeight = SCREEN_HEIGHT;
  435.     VioMode = 25;
  436.  
  437.     while(--ac) {
  438.     av++;
  439.     switch(**av) {
  440.         case 'f':
  441.         case 'F':
  442.         Longnap = LONGNAP / 2;
  443.         Shortnap= SHORTNAP/ 2;
  444.         break;
  445.         case 'm':
  446.         case 'M':
  447.         Longnap = LONGNAP;
  448.         Shortnap= SHORTNAP;
  449.         break;
  450.         case 's':
  451.         case 'S':
  452.         Longnap = LONGNAP * 2;
  453.         Shortnap= SHORTNAP* 2;
  454.         break;
  455.         case '4':        /* Assume 43 line mode was wanted */
  456.         ScreenHeight = 42;
  457.         ScreenWidth  = 79;
  458.         VioMode = 43;
  459.         break;
  460.         case '2':
  461.         ScreenHeight = 24;
  462.         ScreenWidth  = 79;
  463.         VioMode = 25;
  464.         break;
  465.         default:
  466.         return(1);
  467.     }
  468.     }
  469.  
  470.     VioGetCurType(&OldCur, 0);        /* Save old cursor */
  471.  
  472.     modedata.cb = sizeof(modedata); /* change mode as needed */
  473.     VioGetMode( &modedata, 0);
  474.     OldVioMode = modedata;
  475.     modedata.row = VioMode;
  476.     VioSetMode( &modedata, 0);
  477.  
  478.     NewCur.yStart = 0;
  479.     NewCur.cEnd = 0;
  480.     NewCur.cx = 1;
  481.     NewCur.attr = -1;
  482.  
  483.     VioSetCurType( &NewCur, 0 );     /* make cursor go away */
  484.  
  485.     return (0);
  486. }
  487.