|
|
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.