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