home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk10 / apps / chaser / chaser.c next >
Encoding:
C/C++ Source or Header  |  1988-08-11  |  14.0 KB  |  481 lines

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