|
|
1.1.1.2 ! root 1: /* SWARM ! 2: * Created by Microsoft Corp. 1986 ! 3: * ! 4: * the idea behind this game is as follows: 1.1 root 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: * 1.1.1.2 ! root 32: * As the main loop launches threads it puts an ID code into the thread's 1.1 root 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: * 1.1.1.2 ! root 41: * DosSemRequest() VioScrollUp() MouOpen() ! 42: * DosSemClear() VioWrtCellStr() MouSetPtrPos() ! 43: * DosCreateThread() VioSetCurType() MouReadEventQue() ! 44: * DosExit() VioSetMode() ! 45: * DosSleep() 1.1 root 46: */ 1.1.1.2 ! root 47: #include <os2def.h> ! 48: #define INCL_DOSPROCESS ! 49: #define INCL_DOSSEMAPHORES ! 50: #include <bsedos.h> ! 51: #define INCL_SUB ! 52: #include <bsesub.h> 1.1 root 53: #include <malloc.h> 1.1.1.2 ! root 54: #undef NULL 1.1 root 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: 1.1.1.2 ! root 92: HMOU Mouse; /* place for mouse handle */ ! 93: ULONG Shortnap; /* Sleep times for chasers */ ! 94: ULONG Longnap; ! 95: ULONG Semaphore = 0; /* Ram semaphore */ 1.1 root 96: 1.1.1.2 ! root 97: struct _VIOCURSORINFO NewCur; /* struct for setting cursor type */ ! 98: struct _VIOCURSORINFO OldCur; 1.1 root 99: 1.1.1.2 ! root 100: struct _VIOMODEINFO modedata; /* Data saves for VIO mode */ ! 101: struct _VIOMODEINFO OldVioMode; 1.1 root 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]); 1.1.1.2 ! root 126: DosExit(EXIT_THREAD,1); 1.1 root 127: } 128: if (InitGame()) /* Init game, exit if some problem */ 1.1.1.2 ! root 129: DosExit(EXIT_PROCESS,1); 1.1 root 130: 1.1.1.2 ! root 131: Defender(); /* Run mouse loop (defend against the swarm) */ 1.1 root 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: { 1.1.1.2 ! root 148: USHORT ReadType = 1, /* Wait for mouse events */ ! 149: alive, ! 150: i; ! 151: struct _MOUEVENTINFO MouInfo; /* mouse event packet structure */ 1.1 root 152: 153: alive = CHASER; 154: 155: do { 1.1.1.2 ! root 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; 1.1 root 163: } 1.1.1.2 ! root 164: if( MouInfo.fs & 4 ) { /* if left button pressed, */ 1.1 root 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: 1.1.1.2 ! root 171: DosBeep(300,75); /* make a dying sound */ ! 172: DosBeep(600,75); ! 173: DosBeep(300,85); 1.1 root 174: 1.1.1.2 ! root 175: alive--; /* Decrease number alive */ 1.1 root 176: break; /* Can only kill one at a time */ 177: } 178: } 179: } 1.1.1.2 ! root 180: if( MouInfo.fs & 16 ) /* If right button pressed... */ 1.1 root 181: break; /* End game, clean up */ 182: 1.1.1.2 ! root 183: DosSemClear(&Semaphore); 1.1 root 184: } 185: while (GOAL.state == ALIVE && alive); /* loop till all are dead */ 186: } 187: 188: /* 1.1.1.2 ! root 189: * This thread manages the individual attackers. It is spun off as 1.1 root 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: 1.1.1.2 ! root 210: VioWrtCellStr( Chaser, 2, ME.row, ME.col, 0 ); 1.1 root 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)) { 1.1.1.2 ! root 224: DosSleep( 1L ); 1.1 root 225: continue; 226: } 1.1.1.2 ! root 227: DosSemRequest(&Semaphore, WAIT); 1.1 root 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: */ 1.1.1.2 ! root 281: VioWrtCellStr( Blank, 2, ME.row, ME.col, 0 ); ! 282: VioWrtCellStr( Chaser, 2, row, col, 0 ); 1.1 root 283: 1.1.1.2 ! root 284: DosSemClear(&Semaphore); 1.1 root 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)) { 1.1.1.2 ! root 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 ); 1.1 root 300: GOAL.state = DEAD; 301: } 302: /* 303: * Sleep an amount of time that varies depending 304: * upon the danger level 305: */ 306: if( danger ) 1.1.1.2 ! root 307: DosSleep(Shortnap); 1.1 root 308: else 1.1.1.2 ! root 309: DosSleep(Longnap); 1.1 root 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: */ 1.1.1.2 ! root 316: DosSemClear(&Semaphore); 1.1 root 317: 318: if (GOAL.state == ALIVE) { 1.1.1.2 ! root 319: VioWrtCellStr(Blank, 2, ME.row, ME.col, 0 ); 1.1 root 320: } 1.1.1.2 ! root 321: DosExit( EXIT_THREAD ,0); 1.1 root 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: { 1.1.1.2 ! root 333: struct _PTRLOC InitMouPos; 1.1 root 334: void far chasethread(); /* code to control chasers */ 1.1.1.2 ! root 335: PBYTE Tstack; /* stack for new threads */ 1.1 root 336: unsigned chaseID; 337: int i, rc; 338: /* 339: * Clear the screen. 340: */ 1.1.1.2 ! root 341: VioScrollUp( 0, 0, -1, -1, -1, Blank, 0 ); 1.1 root 342: /* 343: * Draw the prize 344: */ 345: GOAL.row = ScreenHeight/2; 346: GOAL.col = ScreenWidth /2; 347: GOAL.state = ALIVE; 1.1.1.2 ! root 348: VioWrtCellStr(Prize, 2, GOAL.row, GOAL.col, 0 ); 1.1 root 349: /* 350: * Open the mouse pointer device and set it's location. 351: */ 1.1.1.2 ! root 352: MouOpen( 0L, &Mouse ); ! 353: InitMouPos.row = GOAL.row; ! 354: InitMouPos.col = GOAL.col; ! 355: MouSetPtrPos( &InitMouPos, Mouse); ! 356: MouDrawPtr(Mouse); 1.1 root 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: */ 1.1.1.2 ! root 373: DosSemRequest(&Semaphore, WAIT); 1.1 root 374: 375: for( i = 0; i < CHASER; i++ ) { /* for each of our threads... */ 376: univ[i].state = ALIVE; /* Set each one alive */ 1.1.1.2 ! root 377: Tstack = (PBYTE)malloc(sizeof(int) * STACKSIZE); 1.1 root 378: if (Tstack == NULL ) { /* Create a stack */ 379: printf( "thread %d stack malloc failed\n", i ); 380: return(1); 381: } 1.1.1.2 ! root 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 */ 1.1 root 385: 1.1.1.2 ! root 386: rc = DosCreateThread(chasethread, &chaseID, Tstack); 1.1 root 387: if(rc) { 388: printf( "create of thread %d failed, error: %d\n", i, rc ); 389: return (1); 390: } 391: } 1.1.1.2 ! root 392: DosSemClear(&Semaphore); 1.1 root 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: 1.1.1.2 ! root 407: DosSleep(1L); /* Yield the machine so attacker can clean up */ ! 408: VioSetMode( &OldVioMode, 0); 1.1 root 409: /* 410: blank[0] = ' '; 411: blank[1] = OldVioMode.color; 1.1.1.2 ! root 412: VioScrollUp( 0, 0, -1, -1, -1, blank, 0 ); 1.1 root 413: */ 1.1.1.2 ! root 414: VioSetCurType( &OldCur, 0); ! 415: DosExit(EXIT_PROCESS,0); /* Exit and terminate all threads. */ 1.1 root 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: { 1.1.1.2 ! root 428: struct _VIOMODEINFO modedata; 1.1 root 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: 1.1.1.2 ! root 470: VioGetCurType(&OldCur, 0); /* Save old cursor */ 1.1 root 471: 1.1.1.2 ! root 472: modedata.cb = sizeof(modedata); /* change mode as needed */ ! 473: VioGetMode( &modedata, 0); 1.1 root 474: OldVioMode = modedata; 475: modedata.row = VioMode; 1.1.1.2 ! root 476: VioSetMode( &modedata, 0); 1.1 root 477: 1.1.1.2 ! root 478: NewCur.yStart = 0; ! 479: NewCur.cEnd = 0; ! 480: NewCur.cx = 1; ! 481: NewCur.attr = -1; 1.1 root 482: 1.1.1.2 ! root 483: VioSetCurType( &NewCur, 0 ); /* make cursor go away */ 1.1 root 484: 485: return (0); 486: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.