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