1 /*
2 * SDLRoids - An Astroids clone.
3 *
4 * Copyright (c) 2000 David Hedbor <david@hedbor.org>
5 * based on xhyperoid by Russel Marks.
6 * xhyperoid is based on a Win16 game, Hyperoid by Edward Hutchins
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 */
18
19 /*
20 * hyperoid.c - Main game backend.
21 */
22
23 #include "config.h"
24 RCSID("$Id: hyperoid.c,v 1.15 2001/03/27 23:23:52 neotron Exp $");
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <signal.h>
30 #include <math.h>
31 #include <time.h>
32 #include <limits.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #ifdef HAVE_SYS_WAIT_H
36 #include <sys/wait.h>
37 #endif
38 #include <sys/time.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41
42 #include "SDL.h"
43 #include "rand.h"
44 #include "getargs.h"
45 #include "misc.h"
46 #include "roidsupp.h"
47 #include "sdlsound.h"
48 #include "graphics.h"
49
50 #include "hyperoid.h"
51
52 static int palrgb[16*3]=
53 {
54 0,0,0, 128,128,128,
55 192,192,192, 255,255,255,
56 128,0,0, 255,0,0,
57 0,128,0, 0,255,0,
58 0,0,128, 0,0,255,
59 128,128,0, 255,255,0,
60 0,128,128, 0,255,255,
61 128,0,128, 255,0,255
62 };
63
64 #define HIT_SHIP 0
65 #define HIT_ROID 1
66 #define HIT_SHOT 2
67
68 /* globals */
69
70 static int nLevel;
71 static int nBadGuys; /* Number of badguys currently alive */
72 static int lHighScore; /* Highscore. Set but not saved or displayed */
73 int bRestart, bPaused=0;
74 PLAYER me; /* The player struct */
75
76 LIST FreeList, RoidList, ShotList, FlameList, SpinnerList;
77 LIST HunterList, HunterShotList, SwarmerList, LetterList, BonusList;
78 int nCos[DEGREE_SIZE], nSin[DEGREE_SIZE]; /* Sinus and cosinus lookup tables */
79 OBJ Obj[MAX_OBJS];
80
81 static int restart_timer_count = 0, game_done = 0;
82
83 /* Directory of the binary */
84 char *bindir;
85
86
87 /* locals */
88
89 int dwSeed;
90 static RECT rectShotClip;
91 static POINT Player[] =
92 { {0, 0}, {160, 150}, {0, 250}, {96, 150}, {0, 0} };
93 static POINT Spinner[] =
94 { {160, 150}, {224, 100}, {96, 100}, {32, 150}, {160, 150} };
95 static POINT Swarmer[] =
96 { {0, 100}, {64, 100}, {128, 100}, {192, 100}, {0, 100} };
97 static POINT Hunter[] =
98 {
99 {160, 150}, {0, 250}, {192, 30}, {64, 30},
100 {0, 250}, {96, 150}, {128, 150}, {160, 150}
101 };
102 static POINT Bonus[] =
103 { {0, 150}, {102, 150}, {205, 150}, {51, 150}, {154, 150}, {0, 150} };
104
105
106 /* KillBadGuy - kill off a badguy (made into a macro) */
107
108 #define KillBadGuy() \
109 ((--nBadGuys <= 0)?(SetRestart( RESTART_NEXTLEVEL ),TRUE):FALSE)
110
111
112 /* my_rand - pseudorandom number from 0 to x-1 (thanks antman!) */
113
114 /* XXX replace? - it's probably v. poor */
115
116 /* AddHead - add an object to the head of a list */
117
AddHead(LIST * npList,NODE * npNode)118 void AddHead( LIST *npList, NODE *npNode )
119 {
120 if (npList->npHead)
121 {
122 npNode->npNext = npList->npHead;
123 npNode->npPrev = NULL;
124 npList->npHead = (npList->npHead->npPrev = npNode);
125 }
126 else /* add to an empty list */
127 {
128 npList->npHead = npList->npTail = npNode;
129 npNode->npNext = npNode->npPrev = NULL;
130 }
131 }
132
133
134 /* RemHead - remove the first element in a list */
135
RemHead(LIST * npList)136 NODE *RemHead( LIST *npList )
137 {
138 if (npList->npHead)
139 {
140 NODE *npNode = npList->npHead;
141 if (npList->npTail != npNode)
142 {
143 npList->npHead = npNode->npNext;
144 npNode->npNext->npPrev = NULL;
145 }
146 else npList->npHead = npList->npTail = NULL;
147 return( npNode );
148 }
149 else return( NULL );
150 }
151
152
153 /* Remove - remove an arbitrary element from a list */
154
Remove(LIST * npList,NODE * npNode)155 void Remove( LIST *npList, NODE *npNode )
156 {
157 if (npNode->npPrev) npNode->npPrev->npNext = npNode->npNext;
158 else npList->npHead = npNode->npNext;
159 if (npNode->npNext) npNode->npNext->npPrev = npNode->npPrev;
160 else npList->npTail = npNode->npPrev;
161 }
162
163
164 /* DrawObject - draw a single object */
165 int foo=0;
DrawObject(OBJ * npObj)166 void DrawObject( OBJ *npObj )
167 {
168 int nCnt;
169 POINT Pts[MAX_PTS];
170 int nDir, x, y;
171 if(bPaused) {
172 nDir = npObj->nDir;
173 x = npObj->Pos.x;
174 y = npObj->Pos.y;
175 } else {
176 nDir = (npObj->nDir += npObj->nSpin);
177 x = (npObj->Pos.x += (npObj->Vel.x));
178 y = (npObj->Pos.y += (npObj->Vel.y));
179 }
180
181 if (x < -CLIP_COORD) npObj->Pos.x = x = CLIP_COORD;
182 else if (x > CLIP_COORD) npObj->Pos.x = x = -CLIP_COORD;
183 if (y < -CLIP_COORD) npObj->Pos.y = y = CLIP_COORD;
184 else if (y > CLIP_COORD) npObj->Pos.y = y = -CLIP_COORD;
185
186 for (nCnt = npObj->byPts - 1; nCnt >= 0; --nCnt)
187 {
188 int wDeg = DEG( npObj->Pts[nCnt].x + nDir );
189 int nLen = npObj->Pts[nCnt].y;
190 Pts[nCnt].x = x + MULDEG( nLen, nCos[wDeg] );
191 Pts[nCnt].y = y + MULDEG( nLen, nSin[wDeg] );
192 }
193 ResetRefreshCoords();
194 if (npObj->byPts > 1)
195 {
196 set_colour(BLACK);
197 Polyline( npObj->Old, npObj->byPts );
198 if (npObj->nCount > 0)
199 {
200 set_colour(npObj->byColor);
201 Polyline( Pts, npObj->byPts );
202 for (nCnt = npObj->byPts - 1; nCnt >= 0; --nCnt)
203 npObj->Old[nCnt] = Pts[nCnt];
204 }
205 }
206 else /* just a point */
207 {
208 SetPixel( npObj->Old[0].x, npObj->Old[0].y, BLACK );
209 if (npObj->nCount > 0)
210 {
211 SetPixel( Pts[0].x, Pts[0].y, npObj->byColor );
212 npObj->Old[0] = Pts[0];
213 }
214 }
215 RedrawObject();
216 }
217
218
219 /* SetRestart - set the restart timer */
220
SetRestart(RESTART_MODE Restart)221 void SetRestart( RESTART_MODE Restart )
222 {
223 POINT Pt;
224 char szBuff[32];
225
226 if (bRestart) return;
227 restart_timer_count=RESTART_DELAY_FRAMES;
228 bRestart = TRUE;
229
230 Pt.x = Pt.y = 0;
231 switch (Restart)
232 {
233 case RESTART_GAME:
234 /* PrintLetters( "STARTING NEW GAME", Pt, Pt, BLUE, 300 ); */
235 break;
236 case RESTART_DEATH:
237 SpinLetters( "GAME OVER", Pt, Pt, RED, 400 );
238 break;
239 case RESTART_LEVEL:
240 PrintLetters( "GET READY", Pt, Pt, BLUE, 300 );
241 break;
242 case RESTART_NEXTLEVEL:
243 sprintf( szBuff, "LEVEL %d", nLevel + 1 );
244 PrintLetters( szBuff, Pt, Pt, BLUE, 300 );
245 break;
246 }
247 }
248
249 /* DeleteBadguys - delete a list of badguys without explosions */
250
DeleteBadguys(LIST * npList)251 void DeleteBadguys( LIST *npList )
252 {
253 OBJ * npObj;
254
255 while ((npObj = HeadObj( npList )))
256 {
257 nBadGuys--;
258 npObj->nCount = 0;
259 DrawObject( npObj );
260 RemoveObj( npList, npObj );
261 AddHeadObj( &FreeList, npObj );
262 }
263 }
264 /* NewGame - start a new game */
265
NewGame(RESTART_MODE restart)266 void NewGame( RESTART_MODE restart )
267 {
268 lock_graphics();
269 game_done = 0;
270 me.Shield.byColor = BLACK;
271 Explode( me.Player );
272 me.Player->nCount = 0;
273 me.Player->byColor = WHITE;
274 SetRestart( restart );
275 DeleteBadguys( &RoidList );
276 DeleteBadguys( &SpinnerList );
277 DeleteBadguys( &SwarmerList );
278 DeleteBadguys( &HunterList );
279 nBadGuys=0;
280 unlock_graphics();
281 }
282
283 /* PrintPlayerMessage - show the player a status message */
284
PrintPlayerMessage(char * npszText)285 void PrintPlayerMessage( char * npszText )
286 {
287 POINT Pos, Vel;
288
289 Pos = me.Player->Pos;
290 Pos.y -= 400;
291 Vel.x = 0;
292 Vel.y = -50;
293 PrintLetters( npszText, Pos, Vel, GREEN, 150 );
294 }
295
296
297 /* AddExtraLife - give the player another life */
298
AddExtraLife(void)299 void AddExtraLife( void )
300 {
301 PrintPlayerMessage( "EXTRA LIFE" );
302 queuesam(EFFECT_CHANNEL,EXTRALIFE_SAMPLE);
303 ++me.Player->nCount;
304 me.Player->byColor = (BYTE)(BLACK + me.Player->nCount);
305 if (me.Player->byColor > WHITE) me.Player->byColor = WHITE;
306 }
307
308
309 /* Hit - something hit an object, do fireworks */
310
Hit(OBJ * npObj)311 void Hit( OBJ *npObj )
312 {
313 int nCnt;
314
315 for (nCnt = 0; nCnt < 6; ++nCnt)
316 {
317 OBJ *npFlame = RemHeadObj( &FreeList );
318 if (!npFlame) return;
319 npFlame->Pos.x = npObj->Pos.x;
320 npFlame->Pos.y = npObj->Pos.y;
321 npFlame->Vel.x = npObj->Vel.x;
322 npFlame->Vel.y = npObj->Vel.y;
323 npFlame->nDir = npObj->nDir + (nCnt * DEGREE_SIZE) / 6;
324 npFlame->nSpin = 0;
325 npFlame->nCount = 10 + my_rand( 8 );
326 npFlame->byColor = YELLOW;
327 npFlame->byPts = 1;
328 npFlame->Pts[0].x = npFlame->Pts[0].y = 0;
329 ACCEL( npFlame, npFlame->nDir, 50 - npFlame->nCount );
330 npFlame->nCount = (npFlame->nCount);
331 AddHeadObj( &FlameList, npFlame );
332 }
333 }
334
335
336 /* Explode - explode an object */
337
Explode(OBJ * npObj)338 void Explode( OBJ *npObj )
339 {
340 int nCnt, nSize = npObj->byPts;
341
342 DrawObject( npObj );
343 for (nCnt = 0; nCnt < nSize; ++nCnt)
344 {
345 OBJ *npFlame;
346 if (my_rand( 2 )) continue;
347 if (!(npFlame = RemHeadObj( &FreeList ))) return;
348 npFlame->Pos.x = npObj->Pos.x;
349 npFlame->Pos.y = npObj->Pos.y;
350 npFlame->Vel.x = npObj->Vel.x;
351 npFlame->Vel.y = npObj->Vel.y;
352 npFlame->nDir = npObj->nDir + nCnt * DEGREE_SIZE / nSize + my_rand( 32 );
353 npFlame->nSpin = my_rand( 31 ) - 15;
354 npFlame->nCount = 25 + my_rand( 16 );
355 npFlame->byColor = npObj->byColor;
356 npFlame->byPts = 2;
357 npFlame->Pts[0] = npObj->Pts[nCnt];
358 if (nCnt == nSize - 1) npFlame->Pts[1] = npObj->Pts[0];
359 else npFlame->Pts[1] = npObj->Pts[nCnt + 1];
360 ACCEL( npFlame, npFlame->nDir, 60 - npFlame->nCount);
361 npFlame->nCount = (npFlame->nCount);
362 AddHeadObj( &FlameList, npFlame );
363 }
364 Hit( npObj );
365 }
366
367
368 /* HitPlayer - blow up the player */
369
HitPlayer(OBJ * npObj,int hittype)370 int HitPlayer( OBJ *npObj, int hittype )
371 {
372 POINT Vel;
373 int nMass, nSpin;
374
375 if (me.Player->nCount <= 0) return( FALSE );
376
377 /* rumble and shake both objects */
378 nMass = me.Player->nMass + npObj->nMass;
379
380 nSpin = me.Player->nSpin + npObj->nSpin;
381 npObj->nSpin -= MulDiv( nSpin, me.Player->nMass, nMass );
382 me.Player->nSpin -= MulDiv( nSpin, npObj->nMass, nMass );
383
384 Vel.x = me.Player->Vel.x - npObj->Vel.x;
385 Vel.y = me.Player->Vel.y - npObj->Vel.y;
386 npObj->Vel.x += MulDiv( Vel.x, me.Player->nMass, nMass );
387 npObj->Vel.y += MulDiv( Vel.y, me.Player->nMass, nMass );
388 me.Player->Vel.x -= MulDiv( Vel.x, npObj->nMass, nMass );
389 me.Player->Vel.y -= MulDiv( Vel.y, npObj->nMass, nMass );
390 if(me.isSafe) {
391 switch(hittype)
392 {
393 case HIT_SHOT:
394 case HIT_SHIP:
395 npObj->nCount = 1;
396 Explode(npObj);
397 queuesam(EFFECT_CHANNEL,PHIT_SAMPLE);
398 break;
399 case HIT_ROID:
400 BreakRoid( npObj, NULL );
401 break;
402 }
403 Hit( me.Player );
404 return FALSE;
405 } else {
406 if (--me.Player->nCount)
407 {
408 me.Player->byColor = (BYTE)(BLACK + me.Player->nCount);
409 if (me.Player->byColor > WHITE) me.Player->byColor = WHITE;
410 Hit( me.Player );
411 queuesam(EFFECT_CHANNEL,PHIT_SAMPLE);
412 return TRUE;
413 }
414
415 /* final death */
416 me.Player->byColor = WHITE;
417 Explode( me.Player );
418 queuesam(EFFECT_CHANNEL,EXPLODE2_SAMPLE);
419 game_done = 1;
420 return FALSE;
421 }
422 }
423
424
425 /* CreateLetter - make a new letter object */
426
CreateLetter(int cLetter,int nSize)427 OBJ *CreateLetter( int cLetter, int nSize )
428 {
429 OBJ *npLtr;
430 int nCnt;
431 char *npDesc;
432
433 if (cLetter >= '0' && cLetter <= '9') npDesc = NumberDesc[cLetter - '0'];
434 else if (cLetter >= 'A' && cLetter <= 'Z') npDesc = LetterDesc[cLetter - 'A'];
435 else if (cLetter >= 'a' && cLetter <= 'z') npDesc = LetterDesc[cLetter - 'a'];
436 else if (cLetter == '.') npDesc = "l";
437 else if (cLetter == '-') npDesc = "fgf";
438 else return( NULL );
439
440 if ((npLtr = RemHeadObj( &FreeList )))
441 {
442 npLtr->nMass = 1;
443 npLtr->nDir = 0;
444 npLtr->nSpin = 0;
445 npLtr->nCount = 40;
446 npLtr->byColor = WHITE;
447 npLtr->byPts = (BYTE)(nCnt = strlen( npDesc ));
448 while (nCnt--)
449 {
450 npLtr->Pts[nCnt] = LetterPart[npDesc[nCnt] - 'a'];
451 npLtr->Pts[nCnt].y = MulDiv( npLtr->Pts[nCnt].y, nSize, LETTER_MAX );
452 }
453 AddHeadObj( &LetterList, npLtr );
454 }
455 return( npLtr );
456 }
457
458
459 /* DrawLetters - draw letters and such */
460
DrawLetters(void)461 void DrawLetters( void )
462 {
463 OBJ *npLtr, *npNext;
464
465 for (npLtr = HeadObj( &LetterList ); npLtr; npLtr = npNext)
466 {
467 npNext = NextObj( npLtr );
468 switch (--npLtr->nCount)
469 {
470 case 3:
471 --npLtr->byColor;
472 break;
473 case 0:
474 RemoveObj( &LetterList, npLtr );
475 AddHeadObj( &FreeList, npLtr );
476 break;
477 }
478 DrawObject( npLtr );
479 }
480 }
481
482
483 /* CreateBonus - make a new bonus object */
484
CreateBonus(void)485 void CreateBonus( void )
486 {
487 OBJ * npBonus;
488 int nCnt;
489
490 if ((npBonus = RemHeadObj( &FreeList )))
491 {
492 queuesam(EFFECT_CHANNEL,NEWBONUS_SAMPLE);
493 npBonus->Pos.x = my_rand( CLIP_COORD * 2 ) - CLIP_COORD;
494 npBonus->Pos.y = -CLIP_COORD;
495 npBonus->Vel.x = npBonus->Vel.y = 0;
496 npBonus->nDir = my_rand( DEGREE_SIZE );
497 npBonus->nSpin = (my_rand( 2 ) ? 12 : -12);
498 npBonus->nCount = (my_rand( 6 ) + 1);
499 npBonus->nDelay = 64 + my_rand( 128 );
500 npBonus->nMass = 1;
501 npBonus->byColor = YELLOW; /*(BYTE)(WHITE + (npBonus->nCount * 2));*/
502 npBonus->byPts = DIM(Bonus);
503 for (nCnt = 0; nCnt < DIM(Bonus); ++nCnt)
504 npBonus->Pts[nCnt] = Bonus[nCnt];
505 ACCEL( npBonus, npBonus->nDir, 30 + nLevel * 2 );
506 AddHeadObj( &BonusList, npBonus );
507 }
508 }
509
510
511 /* DrawBonuses - process and draw the bonus list */
512
DrawBonuses(void)513 void DrawBonuses( void )
514 {
515 OBJ *npBonus, *npNext;
516 static int nNextBonus = 1000;
517
518 if (!bPaused && nBadGuys && (--nNextBonus < 0))
519 {
520 CreateBonus();
521 nNextBonus = 1000;
522 }
523
524 for (npBonus = HeadObj( &BonusList ); npBonus; npBonus = npNext)
525 {
526 OBJ * npShot;
527 int nDelta;
528 RECT rect;
529
530 npNext = NextObj( npBonus );
531
532 MKRECT( &rect, npBonus->Pos, 150 );
533
534 if (PTINRECT( &rect, me.Player->Pos ))
535 {
536 if (me.Player->nCount > 0) switch (npBonus->nCount)
537 {
538 case 1:
539 {
540 char szBuff[32];
541 int lBonus = 1000L * nLevel;
542 if (lBonus == 0) lBonus = 500;
543 me.Score += lBonus;
544 sprintf( szBuff, "%d", lBonus );
545 PrintPlayerMessage( szBuff );
546 }
547 break;
548 case 2:
549 me.Shields += 50;
550 me.ExtraShields = 30;
551 me.Shield.byColor = GREEN;
552 PrintPlayerMessage( "EXTRA SHIELD" );
553 break;
554 case 3:
555 ++me.Bombs;
556 PrintPlayerMessage( "EXTRA BOMB" );
557 break;
558 case 4:
559 AddExtraLife();
560 break;
561 case 5:
562 if(me.Guns < 3) {
563 PrintPlayerMessage( "EXTRA CANNON" );
564 me.Guns++;
565 break;
566 }
567 /* FALLTHROUGH */
568 case 6:
569 me.GunRange += 0.1 * (my_rand(3)+1);
570 PrintPlayerMessage( "INCREASED GUN RANGE" );
571 break;
572 }
573 npBonus->nCount = 0;
574 Explode( npBonus );
575 queuesam(BADDIE_CHANNEL,BONUSGOT_SAMPLE);
576 RemoveObj( &BonusList, npBonus );
577 AddHeadObj( &FreeList, npBonus );
578 }
579 else if (INTRECT(&rect, &rectShotClip))
580 {
581 for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot ))
582 {
583 if (!PTINRECT( &rect, npShot->Pos )) continue;
584 npShot->nCount = 1;
585 npBonus->nCount = 0;
586 Explode( npBonus );
587 queuesam(BADDIE_CHANNEL,BONUSSHOT_SAMPLE);
588 RemoveObj( &BonusList, npBonus );
589 AddHeadObj( &FreeList, npBonus );
590 }
591 }
592 if (npBonus->nCount && --npBonus->nDelay <= 0)
593 {
594 --npBonus->nCount;
595 npBonus->nDelay = 64 + my_rand( 128 );
596 /* npBonus->byColor = (BYTE)(WHITE + (npBonus->nCount * 2)); */
597 if (npBonus->nCount == 0)
598 {
599 Explode( npBonus );
600 queuesam(BADDIE_CHANNEL,BONUSTIMEOUT_SAMPLE);
601 RemoveObj( &BonusList, npBonus );
602 AddHeadObj( &FreeList, npBonus );
603 }
604 }
605 if(!bPaused) {
606 nDelta = me.Player->Pos.x - npBonus->Pos.x;
607 while (nDelta < -16 || nDelta > 16) nDelta /= 2;
608 npBonus->Vel.x += nDelta - npBonus->Vel.x / 16;
609 nDelta = me.Player->Pos.y - npBonus->Pos.y;
610 while (nDelta < -16 || nDelta > 16) nDelta /= 2;
611 npBonus->Vel.y += nDelta - npBonus->Vel.y / 16;
612 }
613 DrawObject( npBonus );
614 }
615 }
616
617
618 /* DrawHunterShots - process and draw the hunter shot list */
619
DrawHunterShots(void)620 void DrawHunterShots( void )
621 {
622 OBJ *npShot, *npNext;
623
624 for (npShot = HeadObj( &HunterShotList ); npShot; npShot = npNext)
625 {
626 RECT rect;
627
628 npNext = NextObj( npShot );
629
630 if(!bPaused) {
631 MKRECT( &rect, npShot->Pos, 200 );
632
633 if (PTINRECT( &rect, me.Player->Pos ))
634 {
635 HitPlayer( npShot, HIT_SHOT );
636 npShot->nCount = 1;
637 }
638 switch (--npShot->nCount)
639 {
640 case 7:
641 npShot->byColor = DKGREEN;
642 break;
643 case 0:
644 RemoveObj( &HunterShotList, npShot );
645 AddHeadObj( &FreeList, npShot );
646 break;
647 }
648 }
649 DrawObject( npShot );
650 }
651 }
652
653
654 /* FireHunterShot - fire a hunter bullet */
655
FireHunterShot(OBJ * npHunt)656 void FireHunterShot( OBJ *npHunt )
657 {
658 OBJ * npShot;
659
660 if ((npShot = RemHeadObj( &FreeList )))
661 {
662 queuesam(BSHOT_CHANNEL,BSHOT_SAMPLE);
663 npShot->Pos.x = npHunt->Pos.x;
664 npShot->Pos.y = npHunt->Pos.y;
665 npShot->Vel.x = npHunt->Vel.x;
666 npShot->Vel.y = npHunt->Vel.y;
667 npShot->nMass = 8;
668 npShot->nDir = npHunt->nDir + my_rand( 5 ) - 2;
669 npShot->nSpin = (my_rand( 2 ) ? 10 : -10);
670 npShot->nCount = (16 + my_rand( 8 ));
671 npShot->byColor = GREEN;
672 npShot->byPts = 2;
673 npShot->Pts[0].x = 128;
674 npShot->Pts[0].y = 50;
675 npShot->Pts[1].x = 0;
676 npShot->Pts[1].y = 50;
677 ACCEL( npShot, npShot->nDir, 200 + npShot->nCount );
678 AddHeadObj( &HunterShotList, npShot );
679 }
680 }
681
682
683 /* CreateHunter - make a new hunter */
684
CreateHunter(void)685 void CreateHunter( void )
686 {
687 OBJ * npHunt;
688 int nCnt;
689
690 if ((npHunt = RemHeadObj( &FreeList )))
691 {
692 queuesam(EFFECT_CHANNEL,NEWHUNT_SAMPLE);
693 npHunt->Pos.x = my_rand( CLIP_COORD * 2 ) - CLIP_COORD;
694 npHunt->Pos.y = -CLIP_COORD;
695 npHunt->Vel.x = npHunt->Vel.y = 0;
696 npHunt->nMass = 256;
697 npHunt->nDir = my_rand( DEGREE_SIZE );
698 npHunt->nSpin = 0;
699 npHunt->nCount = 1 + my_rand( nLevel );
700 npHunt->nDelay = 2 + my_rand( 10 );
701 npHunt->byColor = CYAN;
702 npHunt->byPts = DIM(Hunter);
703 for (nCnt = 0; nCnt < DIM(Hunter); ++nCnt)
704 npHunt->Pts[nCnt] = Hunter[nCnt];
705 ACCEL( npHunt, npHunt->nDir, 30 + nLevel * 2 );
706 AddHeadObj( &HunterList, npHunt );
707 ++nBadGuys;
708 }
709 }
710
711
712 /* DrawHunters - process and draw the hunter list */
713
DrawHunters(void)714 void DrawHunters( void )
715 {
716 OBJ *npHunt, *npNext;
717 static int nNextHunter = 200;
718
719 if (!bPaused && nBadGuys && (--nNextHunter < 0))
720 {
721 CreateHunter();
722 nNextHunter = (1000 + my_rand( 1000 ) - nLevel * 8);
723 }
724
725 for (npHunt = HeadObj( &HunterList ); npHunt; npHunt = npNext)
726 {
727 OBJ * npShot;
728 RECT rect;
729
730 npNext = NextObj( npHunt );
731 if(!bPaused) {
732 MKRECT( &rect, npHunt->Pos, 200 );
733
734 if (PTINRECT( &rect, me.Player->Pos ))
735 {
736 HitPlayer( npHunt, HIT_SHIP );
737 --npHunt->nCount;
738 if (npHunt->nCount < 1)
739 {
740 KillBadGuy();
741 npHunt->byColor = CYAN;
742 Explode( npHunt );
743 queuesam(BADDIE_CHANNEL,HUNTEXPLODE_SAMPLE);
744 RemoveObj( &HunterList, npHunt );
745 AddHeadObj( &FreeList, npHunt );
746 }
747 else if (npHunt->nCount == 1)
748 {
749 npHunt->byColor = DKCYAN;
750 queuesam(BADDIE_CHANNEL,BADDIEWOUND_SAMPLE);
751 }
752 }
753 else if (INTRECT(&rect, &rectShotClip))
754 {
755 for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot ))
756 {
757 if (!PTINRECT( &rect, npShot->Pos )) continue;
758 npShot->nCount = 1;
759 me.Score += npHunt->nCount * 1000;
760 if (--npHunt->nCount < 1)
761 {
762 KillBadGuy();
763 npHunt->byColor = CYAN;
764 Explode( npHunt );
765 queuesam(BADDIE_CHANNEL,HUNTEXPLODE_SAMPLE);
766 RemoveObj( &HunterList, npHunt );
767 AddHeadObj( &FreeList, npHunt );
768 }
769 else
770 {
771 if (npHunt->nCount == 1) npHunt->byColor = DKCYAN;
772 Hit( npHunt );
773 queuesam(BADDIE_CHANNEL,BADDIEWOUND_SAMPLE);
774 }
775 break;
776 }
777 }
778 ACCEL( npHunt, npHunt->nDir, 8 );
779 npHunt->Vel.x -= npHunt->Vel.x / 16;
780 npHunt->Vel.y -= npHunt->Vel.y / 16;
781 if (--npHunt->nDelay <= 0)
782 {
783 npHunt->nDelay = (my_rand( 10 ));
784 npHunt->nSpin = my_rand( 11 ) - 5;
785 FireHunterShot( npHunt );
786 }
787 }
788 DrawObject( npHunt );
789 }
790 }
791
792
793 /* CreateSwarmer - make a new swarmer */
794
CreateSwarmer(POINT Pos,int nDir,int nCount)795 void CreateSwarmer( POINT Pos, int nDir, int nCount )
796 {
797 OBJ * npSwarm;
798 int nCnt;
799
800 if ((npSwarm = RemHeadObj( &FreeList )))
801 {
802 queuesam(EFFECT_CHANNEL,NEWSWARM_SAMPLE);
803 npSwarm->Pos = Pos;
804 npSwarm->Vel.x = npSwarm->Vel.y = 0;
805 npSwarm->nDir = nDir;
806 npSwarm->nSpin = my_rand( 31 ) - 15;
807 npSwarm->nCount = nCount;
808 npSwarm->nDelay = 64 + my_rand( 64 );
809 npSwarm->nMass = 32;
810 npSwarm->byColor = DKGREEN;
811 npSwarm->byPts = DIM(Swarmer);
812 for (nCnt = 0; nCnt < DIM(Swarmer); ++nCnt)
813 {
814 npSwarm->Pts[nCnt] = Swarmer[nCnt];
815 npSwarm->Pts[nCnt].y += nCount * 10;
816 }
817 ACCEL( npSwarm, npSwarm->nDir, 30 + nLevel * 2 );
818 AddHeadObj( &SwarmerList, npSwarm );
819 ++nBadGuys;
820 }
821 }
822
823
824 /* DrawSwarmers - process and draw the swarmer list */
825
DrawSwarmers(void)826 void DrawSwarmers( void )
827 {
828 OBJ *npSwarm, *npNext;
829 static int nNextSwarmer = 1000;
830
831 if (!bPaused && nBadGuys && (--nNextSwarmer < 0))
832 {
833 POINT Pos;
834 Pos.x = my_rand( CLIP_COORD * 2 ) - CLIP_COORD;
835 Pos.y = -CLIP_COORD;
836 CreateSwarmer( Pos, my_rand( DEGREE_SIZE ), 8 + nLevel * 2 );
837 nNextSwarmer = 1000 + my_rand( 500 ) - nLevel * 4;
838 }
839
840 for (npSwarm = HeadObj( &SwarmerList ); npSwarm; npSwarm = npNext)
841 {
842 OBJ * npShot;
843 RECT rect;
844
845 npNext = NextObj( npSwarm );
846
847 if(!bPaused) {
848 MKRECT( &rect, npSwarm->Pos, 150 + npSwarm->nCount * 10 );
849
850 if (PTINRECT( &rect, me.Player->Pos ))
851 {
852 HitPlayer( npSwarm, HIT_SHIP );
853 npSwarm->nCount = 0;
854 }
855 else if (INTRECT(&rect, &rectShotClip))
856 {
857 for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot ))
858 {
859 if (!PTINRECT( &rect, npShot->Pos )) continue;
860 npShot->nCount = 1;
861 me.Score += npSwarm->nCount * 25;
862 npSwarm->nCount = 0;
863 break;
864 }
865 }
866 if (npSwarm->nCount <= 0)
867 {
868 npSwarm->byColor = GREEN;
869 KillBadGuy();
870 Explode( npSwarm );
871 queuesam(BADDIE_CHANNEL,SWARMSPLIT_SAMPLE);
872 RemoveObj( &SwarmerList, npSwarm );
873 AddHeadObj( &FreeList, npSwarm );
874 }
875 else
876 {
877 if ((npSwarm->nCount > 1) && (--npSwarm->nDelay <= 0))
878 {
879 int nDir = my_rand( DEGREE_SIZE );
880 int nCount = npSwarm->nCount / 2;
881 CreateSwarmer( npSwarm->Pos, nDir, nCount );
882 nCount = npSwarm->nCount - nCount;
883 CreateSwarmer( npSwarm->Pos, nDir + 128, nCount );
884 npSwarm->nCount = 0;
885 }
886 DrawObject( npSwarm );
887 }
888 } else
889 DrawObject( npSwarm );
890 }
891 }
892
893
894 /* CreateSpinner - make a new spinner */
895
CreateSpinner(void)896 void CreateSpinner( void )
897 {
898 OBJ * npSpin;
899 int nCnt;
900
901 if ((npSpin = RemHeadObj( &FreeList )))
902 {
903 queuesam(EFFECT_CHANNEL,NEWSPIN_SAMPLE);
904 npSpin->Pos.x = my_rand( CLIP_COORD * 2 ) - CLIP_COORD;
905 npSpin->Pos.y = -CLIP_COORD;
906 npSpin->Vel.x = npSpin->Vel.y = 0;
907 npSpin->nDir = my_rand( DEGREE_SIZE );
908 npSpin->nSpin = -12;
909 npSpin->nCount = 1 + my_rand( nLevel );
910 npSpin->nMass = 64 + npSpin->nCount * 32;
911 npSpin->byColor = (BYTE)(MAGENTA - npSpin->nCount);
912 npSpin->byPts = DIM(Spinner);
913 for (nCnt = 0; nCnt < DIM(Spinner); ++nCnt)
914 npSpin->Pts[nCnt] = Spinner[nCnt];
915 ACCEL( npSpin, npSpin->nDir, 30 + nLevel * 2 );
916 AddHeadObj( &SpinnerList, npSpin );
917 ++nBadGuys;
918 }
919 }
920
921
922 /* DrawSpinners - process and draw the spinner list */
923
DrawSpinners(void)924 void DrawSpinners( void )
925 {
926 OBJ *npSpin, *npNext;
927 static int nNextSpinner = 1000;
928
929 if (!bPaused && nBadGuys && (--nNextSpinner < 0))
930 {
931 CreateSpinner();
932 nNextSpinner = 100 + my_rand( 900 ) - nLevel * 2;
933 }
934
935 for (npSpin = HeadObj( &SpinnerList ); npSpin; npSpin = npNext)
936 {
937 OBJ * npShot;
938 int nDelta;
939 RECT rect;
940
941 npNext = NextObj( npSpin );
942 if(!bPaused) {
943 MKRECT( &rect, npSpin->Pos, 150 );
944
945 if (PTINRECT( &rect, me.Player->Pos ))
946 {
947 HitPlayer( npSpin, HIT_SHIP );
948 --npSpin->nCount;
949 npSpin->byColor = (BYTE)(MAGENTA - npSpin->nCount);
950 if (npSpin->nCount < 1)
951 {
952 KillBadGuy();
953 Explode( npSpin );
954 queuesam(BADDIE_CHANNEL,SPINEXPLODE_SAMPLE);
955 RemoveObj( &SpinnerList, npSpin );
956 AddHeadObj( &FreeList, npSpin );
957 }
958 else
959 {
960 queuesam(BADDIE_CHANNEL,BADDIEWOUND_SAMPLE);
961 }
962 }
963 else if (INTRECT(&rect, &rectShotClip))
964 {
965 for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot ))
966 {
967 if (!PTINRECT( &rect, npShot->Pos )) continue;
968 npShot->nCount = 1;
969 me.Score += npSpin->nCount * 500;
970 npSpin->byColor = (BYTE)(MAGENTA - (--npSpin->nCount));
971 if (npSpin->nCount < 1)
972 {
973 KillBadGuy();
974 Explode( npSpin );
975 queuesam(BADDIE_CHANNEL,SPINEXPLODE_SAMPLE);
976 RemoveObj( &SpinnerList, npSpin );
977 AddHeadObj( &FreeList, npSpin );
978 }
979 else
980 {
981 Hit( npSpin );
982 queuesam(BADDIE_CHANNEL,BADDIEWOUND_SAMPLE);
983 }
984 break;
985 }
986 }
987 nDelta = me.Player->Pos.x - npSpin->Pos.x;
988 while (nDelta < -16 || nDelta > 16) nDelta /= 2;
989 npSpin->Vel.x += nDelta - npSpin->Vel.x / 16;
990 nDelta = me.Player->Pos.y - npSpin->Pos.y;
991 while (nDelta < -16 || nDelta > 16) nDelta /= 2;
992 npSpin->Vel.y += nDelta - npSpin->Vel.y / 16;
993 }
994 DrawObject( npSpin );
995 }
996 }
997
998
999 /* CreateRoid - make a new asteroid */
1000
CreateRoid(POINT Pos,POINT Vel,int nSides,BYTE byColor,int nDir,int nSpeed,int nSpin)1001 void CreateRoid( POINT Pos, POINT Vel, int nSides, BYTE byColor,
1002 int nDir, int nSpeed, int nSpin )
1003 {
1004 OBJ * npRoid;
1005 int nCnt;
1006 if ((npRoid = RemHeadObj( &FreeList )))
1007 {
1008 npRoid->Pos = Pos;
1009 npRoid->Vel = Vel;
1010 npRoid->nMass = nSides * 128;
1011 npRoid->nDir = nDir;
1012 npRoid->nSpin = nSpin + my_rand( 11 ) - 5;
1013 npRoid->nCount = nSides * 100;
1014 npRoid->byColor = byColor;
1015 npRoid->byPts = (BYTE)(nSides + 1);
1016 for (nCnt = 0; nCnt < nSides; ++nCnt)
1017 {
1018 npRoid->Pts[nCnt].x = nCnt * DEGREE_SIZE / nSides + my_rand( 30 );
1019 npRoid->Pts[nCnt].y = (nSides - 1) * 100 + 20 + my_rand( 80 );
1020 }
1021 npRoid->Pts[nSides] = npRoid->Pts[0];
1022 ACCEL( npRoid, nDir, nSpeed );
1023 AddHeadObj( &RoidList, npRoid );
1024 ++nBadGuys;
1025 }
1026 }
1027
1028
1029 /* BreakRoid - break up an asteroid */
1030
BreakRoid(OBJ * npRoid,OBJ * npShot)1031 void BreakRoid( OBJ *npRoid, OBJ *npShot )
1032 {
1033 int nCnt, nNew;
1034
1035 me.Score += npRoid->nCount;
1036 if (npShot) npShot->nCount = 1;
1037 switch (npRoid->byPts)
1038 {
1039 case 8:
1040 nNew = 2 + my_rand( 3 );
1041 break;
1042 case 7:
1043 nNew = 1 + my_rand( 3 );
1044 break;
1045 case 6:
1046 nNew = 1 + my_rand( 2 );
1047 break;
1048 case 5:
1049 nNew = my_rand( 2 );
1050 break;
1051 default:
1052 nNew = 0;
1053 break;
1054 }
1055 if (nNew == 1) /* don't explode outward */
1056 {
1057 POINT Pt = npRoid->Pos;
1058 Pt.x += my_rand( 301 ) - 150; Pt.y += my_rand( 301 ) - 150;
1059 CreateRoid( Pt, npRoid->Vel, npRoid->byPts - (nNew + 1),
1060 npRoid->byColor, npShot?(npShot->nDir):npRoid->nDir,
1061 8, npRoid->nSpin );
1062 }
1063 else if (nNew > 0)
1064 {
1065 int nSpeed = npRoid->nSpin * npRoid->nSpin * nNew + 16;
1066 for (nCnt = 0; nCnt < nNew; ++nCnt)
1067 {
1068 POINT Pt = npRoid->Pos;
1069 Pt.x += my_rand( 601 ) - 300; Pt.y += my_rand( 601 ) - 300;
1070 CreateRoid( Pt, npRoid->Vel, npRoid->byPts - (nNew + 1),
1071 npRoid->byColor,
1072 npRoid->nDir + nCnt * DEGREE_SIZE / nNew + my_rand( 32 ),
1073 nSpeed + my_rand( nLevel * 4 ),
1074 npRoid->nSpin / 2 );
1075 }
1076 }
1077 KillBadGuy();
1078 ++npRoid->byColor;
1079 npRoid->nCount = 0;
1080 if (nNew)
1081 {
1082 Hit( npRoid );
1083 DrawObject( npRoid );
1084 queuesam(ASTEROID_CHANNEL,ROIDSPLIT_SAMPLE);
1085 }
1086 else
1087 {
1088 Explode( npRoid );
1089 queuesam(ASTEROID_CHANNEL,ROIDNOSPLIT_SAMPLE);
1090 }
1091 RemoveObj( &RoidList, npRoid );
1092 AddHeadObj( &FreeList, npRoid );
1093 }
1094
1095
1096 /* DrawRoids - process and draw the asteroid list */
1097
DrawRoids(void)1098 void DrawRoids( void )
1099 {
1100 OBJ *npRoid, *npNext;
1101
1102 for (npRoid = HeadObj( &RoidList ); npRoid; npRoid = npNext)
1103 {
1104 int nSize = npRoid->nCount;
1105 OBJ * npShot;
1106 RECT rect;
1107
1108 npNext = NextObj( npRoid );
1109
1110 DrawObject( npRoid );
1111 if(bPaused)
1112 continue;
1113 MKRECT( &rect, npRoid->Pos, nSize );
1114
1115 if (PTINRECT( &rect, me.Player->Pos ) && HitPlayer( npRoid, HIT_ROID ))
1116 {
1117 me.Player->nCount = -me.Player->nCount;
1118 me.Player->byColor = WHITE;
1119 Explode( me.Player );
1120 BreakRoid( npRoid, NULL );
1121 if (nBadGuys) SetRestart( RESTART_LEVEL );
1122 else SetRestart( RESTART_NEXTLEVEL );
1123 }
1124 else if (INTRECT(&rect, &rectShotClip))
1125 {
1126 for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot ))
1127 {
1128 if (!PTINRECT( &rect, npShot->Pos )) continue;
1129 BreakRoid( npRoid, npShot );
1130 break;
1131 }
1132 }
1133 }
1134 }
1135
1136
1137 /* DrawShots - process and draw the player shot list */
1138
DrawShots(void)1139 void DrawShots( void )
1140 {
1141 OBJ *npShot, *npNext;
1142
1143 if ((npShot = HeadObj( &ShotList )))
1144 {
1145 rectShotClip.left = rectShotClip.right = npShot->Pos.x;
1146 rectShotClip.top = rectShotClip.bottom = npShot->Pos.y;
1147 while (npShot)
1148 {
1149 npNext = NextObj( npShot );
1150 if(!bPaused) {
1151 switch (--npShot->nCount)
1152 {
1153 case 10:
1154 npShot->byColor = DKCYAN;
1155 break;
1156 case 5:
1157 npShot->byColor = DKBLUE;
1158 break;
1159 case 0:
1160 RemoveObj( &ShotList, npShot );
1161 AddHeadObj( &FreeList, npShot );
1162 break;
1163 }
1164 }
1165 DrawObject( npShot );
1166 if(!bPaused) {
1167 if (npShot->Pos.x < rectShotClip.left)
1168 rectShotClip.left = npShot->Pos.x;
1169 else if (npShot->Pos.x > rectShotClip.right)
1170 rectShotClip.right = npShot->Pos.x;
1171 if (npShot->Pos.y < rectShotClip.top)
1172 rectShotClip.top = npShot->Pos.y;
1173 else if (npShot->Pos.y > rectShotClip.bottom)
1174 rectShotClip.bottom = npShot->Pos.y;
1175 }
1176 npShot = npNext;
1177 }
1178 }
1179 else
1180 rectShotClip.left = rectShotClip.right =
1181 rectShotClip.top = rectShotClip.bottom = 32767;
1182 }
1183
1184
1185 /* DrawFlames - process and draw the flame list */
1186
DrawFlames(void)1187 void DrawFlames( void )
1188 {
1189 OBJ *npFlame, *npNext;
1190
1191 for (npFlame = HeadObj( &FlameList ); npFlame; npFlame = npNext)
1192 {
1193 npNext = NextObj( npFlame );
1194 if(!bPaused) {
1195 switch (--npFlame->nCount)
1196 {
1197 case 7:
1198 npFlame->byColor = RED;
1199 break;
1200 case 3:
1201 npFlame->byColor = DKRED;
1202 break;
1203 case 0:
1204 RemoveObj( &FlameList, npFlame );
1205 AddHeadObj( &FreeList, npFlame );
1206 break;
1207 }
1208 }
1209 DrawObject( npFlame );
1210 }
1211 }
1212
1213
1214 /* FireShot - fire a bullet */
1215
LowFireShot(int nDir,int nCount)1216 void LowFireShot(int nDir, int nCount)
1217 {
1218 OBJ * npShot;
1219 if ((npShot = RemHeadObj( &FreeList )))
1220 {
1221 npShot->Pos.x = me.Player->Pos.x;
1222 npShot->Pos.y = me.Player->Pos.y;
1223 npShot->Vel.x = me.Player->Vel.x;
1224 npShot->Vel.y = me.Player->Vel.y;
1225 npShot->nMass = 8;
1226 npShot->nDir = nDir;
1227 npShot->nSpin = 0;
1228 npShot->nCount = nCount;
1229 npShot->byColor = CYAN;
1230 npShot->byPts = 2;
1231 npShot->Pts[0].x = 128;
1232 npShot->Pts[0].y = 50;
1233 npShot->Pts[1].x = 0;
1234 npShot->Pts[1].y = 50;
1235 ACCEL( npShot, npShot->nDir, 200 + npShot->nCount );
1236 AddHeadObj( &ShotList, npShot );
1237 }
1238 }
1239
FireShot(void)1240 void FireShot( void )
1241 {
1242 int nDir, nCount;
1243
1244 nDir = me.Player->nDir + my_rand( 5 ) - 2;
1245 nCount = (int)(me.GunRange * (16 + my_rand( 8 )));
1246 queuesam(PSHOT_CHANNEL, PSHOT_SAMPLE);
1247 switch(me.Guns) {
1248 case 1:
1249 LowFireShot(nDir, nCount);
1250 break;
1251 case 2:
1252 LowFireShot(nDir-5, nCount);
1253 LowFireShot(nDir+5, nCount);
1254 break;
1255 case 3:
1256 LowFireShot(nDir, nCount);
1257 LowFireShot(nDir-15, nCount-5);
1258 LowFireShot(nDir+15, nCount-5);
1259 }
1260 }
1261
1262
1263 /* AccelPlayer - move the player forward */
1264
AccelPlayer(int nDir,int nAccel)1265 void AccelPlayer( int nDir, int nAccel )
1266 {
1267 OBJ * npFlame;
1268
1269 /* queuesam(PTHRUST_CHANNEL,PTHRUST_SAMPLE);*/
1270 nDir += me.Player->nDir;
1271 if (nAccel) ACCEL( me.Player, nDir, nAccel );
1272 if ((npFlame = RemHeadObj( &FreeList )))
1273 {
1274 npFlame->Pos.x = me.Player->Pos.x;
1275 npFlame->Pos.y = me.Player->Pos.y;
1276 npFlame->Vel.x = me.Player->Vel.x;
1277 npFlame->Vel.y = me.Player->Vel.y;
1278 npFlame->nDir = nDir + 100 + my_rand( 57 );
1279 npFlame->nSpin = 0;
1280 npFlame->nCount = (nAccel + my_rand( 7 ));
1281 npFlame->byColor = YELLOW;
1282 npFlame->byPts = 1;
1283 npFlame->Pts[0].x = npFlame->Pts[0].y = 0;
1284 ACCEL( npFlame, npFlame->nDir, 50 + my_rand( 10 ) );
1285 AddHeadObj( &FlameList, npFlame );
1286 }
1287 }
1288
1289
1290 /* HitList - Hit() a list of things */
1291
HitList(LIST * npList)1292 void HitList( LIST *npList )
1293 {
1294 OBJ * npObj;
1295
1296 for (npObj = HeadObj( npList ); npObj; npObj = NextObj( npObj ))
1297 if (npObj->nCount) Hit( npObj );
1298 }
1299
1300
1301 /* ExplodeBadguys - explode a list of badguys */
1302
ExplodeBadguys(LIST * npList)1303 void ExplodeBadguys( LIST *npList )
1304 {
1305 OBJ * npObj;
1306
1307 while ((npObj = HeadObj( npList )))
1308 {
1309 KillBadGuy();
1310 npObj->nCount = 0;
1311 Explode( npObj );
1312 RemoveObj( npList, npObj );
1313 AddHeadObj( &FreeList, npObj );
1314 }
1315 }
1316
1317
1318 /* DrawShield - draw the shield around the player */
DrawShield(void)1319 void DrawShield( void ) {
1320 ResetRefreshCoords();
1321 if(me.Shield.Old.x != -1)
1322 /* Must delete old shield */
1323 {
1324 set_colour(BLACK);
1325 Circle(me.Shield.Old.x, me.Shield.Old.y, me.Shield.Radius);
1326 }
1327 if(me.Shield.byColor == BLACK) {
1328 /* Shield gone..*/
1329 me.Shield.Old.x = -1;
1330 } else {
1331 set_colour(me.Shield.byColor);
1332 Circle(me.Shield.Pos.x, me.Shield.Pos.y, me.Shield.Radius);
1333 me.Shield.Old.x = me.Shield.Pos.x;
1334 me.Shield.Old.y = me.Shield.Pos.y;
1335 }
1336 RedrawObject();
1337 }
1338
1339
1340 /* DrawPlayer - process and draw the player */
1341
DrawPlayer(void)1342 void DrawPlayer( void )
1343 {
1344 static int nBombing = 0;
1345 static int nShotDelay = 0;
1346 float keyval;
1347 if (me.Player->nCount <= 0) return;
1348 if(!bPaused) {
1349 if (me.ExtraShields) {
1350 me.Shield.byColor = GREEN;
1351 me.ExtraShields--;
1352 me.isSafe = 1;
1353 } else if(IsKeyDown( KEY_TAB ) && me.Shields) {
1354 me.Shield.byColor = GREEN;
1355 me.Shields--;
1356 me.isSafe = 1;
1357 } else if(me.Shield.byColor != BLACK) {
1358 me.Shield.byColor = BLACK;
1359 me.isSafe = 0;
1360 DrawShield();
1361 }
1362 if (nBombing > 0)
1363 {
1364 if (--nBombing == 0)
1365 {
1366 ExplodeBadguys( &SpinnerList );
1367 ExplodeBadguys( &SwarmerList );
1368 ExplodeBadguys( &HunterList );
1369 queuesam(EFFECT_CHANNEL,EXPLODE2_SAMPLE);
1370 }
1371 else
1372 {
1373 HitList( &SpinnerList );
1374 HitList( &SwarmerList );
1375 HitList( &HunterList );
1376 }
1377 }
1378 else if (me.Bombs && IsKeyDown( KEY_S )) --me.Bombs, nBombing = (5);
1379
1380 if ( (keyval = IsKeyDown( KEY_LEFT )) ) {
1381 me.Player->nSpin += (int)(keyval * 8);
1382 } else if ( (keyval = IsKeyDown( KEY_RIGHT )) ) {
1383 me.Player->nSpin -= (int)(keyval * 8);
1384 }
1385 if ( (keyval = IsKeyDown( KEY_UP )) ) {
1386 AccelPlayer( 0, (int)(keyval * 12) );
1387 }
1388 else if ( (keyval = IsKeyDown( KEY_DOWN )) ) {
1389 AccelPlayer( 128, (int)(keyval * 12));
1390 }
1391 if (!bPaused && nShotDelay) --nShotDelay;
1392 else if (IsKeyDown( KEY_SPACE )) FireShot(), nShotDelay = (2);
1393 }
1394 DrawObject( me.Player );
1395 if(me.Shield.byColor != BLACK) {
1396 me.Shield.Pos.y = me.Player->Pos.y;
1397 me.Shield.Pos.x = me.Player->Pos.x;
1398 DrawShield();
1399 }
1400 if(!bPaused)
1401 me.Player->nSpin /= 2;
1402 }
1403
1404
1405
1406 /* DrawObjects - transform and redraw everything in the system */
1407
DrawObjects(void)1408 void DrawObjects( void )
1409 {
1410 /* move and draw things (I don't think the order is important...) */
1411 lock_graphics();
1412 DrawPlayer();
1413 DrawFlames();
1414 DrawShots();
1415 DrawRoids();
1416 DrawSpinners();
1417 DrawSwarmers();
1418 DrawHunters();
1419 DrawHunterShots();
1420 DrawBonuses();
1421 DrawLetters();
1422 unlock_graphics();
1423 /* (...but I'm not changing it!!! :-) */
1424 }
1425
1426
1427 /* CheckScore - show the score and such stuff */
1428
CheckScore(void)1429 void CheckScore( void )
1430 {
1431 int nLives;
1432
1433 if (me.Score - me.LastLife > EXTRA_LIFE)
1434 {
1435 AddExtraLife();
1436 me.LastLife = me.Score;
1437 }
1438
1439 /* apparently, -ve player lives means we're starting a new
1440 * life soon (ouch). -rjm
1441 */
1442 nLives=((me.Player->nCount > 0) ? me.Player->nCount : -me.Player->nCount);
1443
1444 /* actually do the score/lives/etc-drawing */
1445 score_graphics(nLevel,me.Score,nLives,me.Shields,me.Bombs);
1446 }
1447
1448 /* RestartHyperoid - set up a game! */
1449
RestartHyperoid(void)1450 void RestartHyperoid( void )
1451 {
1452 if (me.Player->nCount == 0)
1453 { /* Player died, this is a new game */
1454 POINT Pos, Vel;
1455 Pos.x = 0;
1456 Pos.y = -CLIP_COORD / 2;
1457 Vel.x = 0;
1458 Vel.y = 150;
1459 PrintLetters( VERSION, Pos, Vel, YELLOW, 800 );
1460 Vel.y = -150;
1461 Pos.y = CLIP_COORD/2;
1462 PrintLetters( "SDLROIDS", Pos, Vel, YELLOW, 800 );
1463 queuesam(BADDIE_CHANNEL,TITLE_SAMPLE);
1464 queuesam(BSHOT_CHANNEL,TITLE_SAMPLE);
1465 me.Player->nCount = 3;
1466 if (lHighScore < me.Score) lHighScore = me.Score;
1467 me.LastLife = me.Score = 0;
1468 nLevel = 0;
1469 me.Shields = 150;
1470 me.Bombs = 3;
1471 me.Guns = 1;
1472 me.GunRange = 1.0;
1473 }
1474 else if (me.Player->nCount < 0)
1475 {
1476 /* cheesy way of restarting after a major collision */
1477 me.Player->nCount = -me.Player->nCount;
1478 }
1479 me.Player->Pos.x = me.Player->Pos.y = 0;
1480 me.Player->Vel.x = me.Player->Vel.y = 0;
1481 me.Player->nDir = 64;
1482 me.Player->nSpin = 0;
1483 me.Player->byColor = WHITE;
1484 me.ExtraShields = 30;
1485 me.Shield.byColor = GREEN;
1486 if (ShotList.npHead)
1487 {
1488 OBJ *npShot;
1489 for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot ))
1490 npShot->nCount = 1;
1491 }
1492
1493 /* reseed the asteroid field */
1494 if (nBadGuys == 0)
1495 {
1496 int nCnt;
1497 ++nLevel;
1498 for (nCnt = 5 + nLevel; nCnt; --nCnt)
1499 {
1500 POINT Pos, Vel;
1501 Pos.x = my_rand( MAX_COORD * 2 ) - MAX_COORD;
1502 Pos.y = my_rand( MAX_COORD * 2 ) - MAX_COORD;
1503 Vel.x = Vel.y = 0;
1504 CreateRoid( Pos, Vel, 6 + my_rand( 2 ),
1505 (BYTE)(my_rand( 2 ) ? DKYELLOW : DKGREY),
1506 my_rand( DEGREE_MAX ), 30 + my_rand( nLevel * 8 ), 0 );
1507 }
1508 }
1509 }
1510
1511
1512
1513 /* InitHyperoid - initialize everything */
1514
InitHyperoid(void)1515 void InitHyperoid( void )
1516 {
1517 double dRad;
1518 int nCnt;
1519
1520 /* seed the randomizer */
1521 dwSeed = time(NULL); /* XXX GetCurrentTime(); */
1522
1523 /* create the lookup table */
1524 for (nCnt = 0; nCnt < DEGREE_SIZE; ++nCnt)
1525 {
1526 dRad = nCnt * 6.2831855 / DEGREE_SIZE;
1527 nCos[nCnt] = (int)(DEGREE_MAX * cos( dRad ));
1528 nSin[nCnt] = (int)(DEGREE_MAX * sin( dRad ));
1529 }
1530
1531 /* allocate all objects as free */
1532 for (nCnt = 0; nCnt < MAX_OBJS; ++nCnt)
1533 AddHeadObj( &FreeList, &(Obj[nCnt]) );
1534
1535 /* set up the player */
1536 me.Player = RemHeadObj( &FreeList );
1537 me.Player->byPts = DIM(Player);
1538 me.Player->nMass = 256;
1539 for (nCnt = 0; nCnt < DIM(Player); ++nCnt)
1540 me.Player->Pts[nCnt] = Player[nCnt];
1541 me.Shield.Radius = 200;
1542 me.Shield.Old.x = -1;
1543 }
1544
do_sleep()1545 void do_sleep()
1546 {
1547 static int lasttick = 0;
1548 int frametime;
1549 if(!lasttick) {
1550 lasttick = SDL_GetTicks();
1551 return;
1552 }
1553 frametime = SDL_GetTicks() -lasttick;
1554 if(frametime <50) SDL_Delay(50-frametime );
1555 lasttick = SDL_GetTicks();
1556 }
1557
main(int argc,char * argv[])1558 int main(int argc,char *argv[])
1559 {
1560 int quit=0, framecount=0, i;
1561 Uint32 start, timed;
1562 getargs(argc, argv);
1563 my_srand(time(NULL));
1564 /* find the last slash */
1565 for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; i--)
1566 ;
1567 if(i > 0) {
1568 bindir = malloc(i+2);
1569 strncpy(bindir, argv[0], i+1);
1570 }
1571 else bindir = NULL;
1572
1573 init_graphics(palrgb);
1574 if(!ARG_NOSND) init_sound();
1575
1576 InitHyperoid();
1577 RestartHyperoid();
1578 start = SDL_GetTicks();
1579 while(!quit)
1580 {
1581 if(ARG_BENCH==0) {
1582 do_sleep();
1583 }
1584
1585 else if((++framecount) >= ARG_BENCH)
1586 quit = 1;
1587 if(!bPaused) {
1588 DrawObjects();
1589 CheckScore();
1590 }
1591 update_graphics();
1592 if(bRestart)
1593 {
1594 restart_timer_count--;
1595 if(restart_timer_count==0)
1596 {
1597 bRestart = FALSE;
1598 bPaused = 0;
1599 RestartHyperoid();
1600 }
1601 }
1602 if(game_done) NewGame(RESTART_DEATH);
1603 else if(IsKeyDown(KEY_F1)) {
1604 NewGame(RESTART_GAME);
1605 update_graphics();
1606 }
1607 if(IsKeyDown(KEY_ESC)) quit=1;
1608 }
1609 timed = SDL_GetTicks() - start;
1610 if(ARG_BENCH)
1611 printf("\r%10d frames in %5.2f seconds, %f fps.\n",
1612 framecount, timed/1000.0,
1613 (float)framecount / (timed / 1000.0));
1614
1615 exit_sound();
1616 exit_graphics();
1617 if(bindir != NULL) free(bindir);
1618 exit(0);
1619 }
1620