1 /* Copyright (c) 1995-1998 Joe Rumsey (mrogre@mediaone.net) */
2 #include "copyright.h"
3 /*
4  * main.c for XGalaga
5  */
6 #include <config.h>
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #ifdef HAVE_SYS_TIME_H
12 #include <sys/time.h>
13 #endif
14 #ifdef HAVE_UNISTD_H
15 # include <unistd.h>
16 #endif
17 #include "libsprite/Wlib.h"
18 #include "struct.h"
19 #include "data.h"
20 #include "images.h"
21 #include "defs.h"
22 #include "paths.h"
23 #include "proto.h"
24 #include "sound.h"
25 
26 struct torp torps[MAXTORPS];
27 struct star stars[NUMSTARS];
28 struct torp *first_etorp=0;
29 
30 static int convoyx = 0, convoymove = 1;
31 static int livecount = 0;
32 static int starspeed = 1;
33 static int attacking = 0, maxattacking, entering=0;
34 static int maxetorps = 5, numetorps=0;
35 static int plflash = 50;
36 static int fullscreen = 0;
37 #ifndef ORIGINAL_XGALAGA
38 static int shots = 0;
39 static int hits = 0;
40 #endif
41 
42 #define convoy_x_pos(i) (convoyx+(20 * (i - 10 * (i/10))))
43 #define convoy_y_pos(i) (20 + (20*(i/10)))
44 
45 static int moves[16][2] = {
46     {0,-4},
47     {1,-4},
48     {3,-3},
49     {4,-1},
50     {4,0},
51     {4,1},
52     {3,3},
53     {1,4},
54     {0,4},
55     {-1,4},
56     {-3,3},
57     {-4,1},
58     {-4,0},
59     {-4,-1},
60     {-3,-3},
61     {-1,-4}
62 };
63 
64 void
xgal_exit(int v)65 xgal_exit(int v)
66 {
67     /*    W_AutoRepeatOn();*/
68     /* Destroy our main window so the fullscreen mode gets unset if we're
69        running fullscreen. (We should really clean up much more here!) */
70     W_DestroyWindow(shellWin);
71 #ifdef SOUND
72     kill_sound();
73 #endif
74     /*    sleep(1);*/ /* Without this, the auto-repeat request fails on my machine... */
75     /* Note if we ever need this autorepeat thingie again, the sleep can and should be replaced by a call to XSync() */
76     exit(v);
77 }
78 
79 
print_usage()80 static void print_usage()
81 {
82 #ifdef SOUND
83 #define PRINT_SOUND	"-nosound             Turn sound OFF\n"
84 #else
85 #define PRINT_SOUND	""
86 #endif
87 
88     printf("\
89 XGalaga v%s\n\
90 Copyright (c) 1995-1998 Joe Rumsey\n\
91 Contributions by Hermann Riedel\n\
92 Command line options:\n\
93 -scores              Prints out the high score files and exits\n\
94 -display <display>   Set your display\n\
95 -mouse               Use mouse control (same as 'm' at the title screen)\n\
96 -keyboard            Use keyboard control (same as 'k')\n%s\
97 -level <number>      Choose starting level (>= 1)\n\
98 -window              Start in windowed mode instead of fullscreen\n\
99 -winsize <WxH>       Window size (default 468 x 596)\n\
100 -b                   turn buffered mode off, use this if it runs\n\
101 too slowly.  Will cause flicker, maybe lots,\n\
102 maybe only a little.\n\
103 \n\
104 This game is now free software, under the GPL\n\
105 \n\
106 Basic instructions:\n\
107 It's Galaga, you know how to play Galaga, stop bothering me.\n\
108 (Ship follows the mouse, button fires.  Auto-fire by holding it\n\
109 down, so no-one accuses me of breaking their mouse!)\n\
110 \n\
111 Keyboard commands:\n\
112 \n\
113 p - pauses\n\
114 q - end this game\n\
115 b - Toggle buffering (flicker vs. speed.)\n\
116 alt + enter - Toggle fullscreen - window\n", VERSION,
117 	PRINT_SOUND
118            );
119 }
120 
121 /*------------------stars-----------------*/
init_stars()122 static void init_stars()
123 {
124     int i;
125 
126     for(i=0;i<NUMSTARS;i++) {
127         stars[i].x = random()%WINWIDTH;
128         stars[i].y = random()%WINHEIGHT;
129         stars[i].speed = (random()%3)+1;
130         switch(random()%5) {
131         case 0:
132             stars[i].color = W_White;
133             break;
134         case 1:
135             stars[i].color = W_Green;
136             break;
137         case 2:
138             stars[i].color = W_Cyan;
139             break;
140         case 3:
141             stars[i].color = W_Red;
142             break;
143         default:
144             stars[i].color = W_Yellow;
145             break;
146         }
147     }
148 }
149 
150 static int drewlevel = 0;
151 
undo_stars()152 static void undo_stars()
153 {
154     int i;
155 
156     if(wantStars) {
157         for(i=0;i<NUMSTARS;i++) {
158             W_DrawPoint(baseWin, stars[i].x, stars[i].y, W_Black);
159         }
160     }
161     if(drewlevel) {
162         W_ClearArea(baseWin, WINWIDTH/2 - (W_StringWidth("LEVEL 000", W_BigFont)/2), WINHEIGHT/2-W_BigTextheight/2,
163                     10*W_BigTextwidth, W_BigTextheight);
164         drewlevel = 0;
165     }
166 }
167 
do_stars()168 static void do_stars()
169 {
170     int i;
171 
172     if(wantStars) {
173         for(i=0;i<NUMSTARS;i++) {
174             if(!paused)
175                 stars[i].y+=stars[i].speed*((starspeed < 20) ? ABS(starspeed) : 20);
176             if(stars[i].y >= WINHEIGHT) {
177                 stars[i].y-=WINHEIGHT+starspeed;
178                 stars[i].x = random() % WINWIDTH;
179                 switch(random()%5) {
180                 case 0:
181                     stars[i].color = W_White;
182                     break;
183                 case 1:
184                     stars[i].color = W_Green;
185                     break;
186                 case 2:
187                     stars[i].color = W_Cyan;
188                     break;
189                 case 3:
190                     stars[i].color = W_Red;
191                     break;
192                 default:
193                     stars[i].color = W_Yellow;
194                     break;
195                 }
196             }
197             W_DrawPoint(baseWin, stars[i].x, stars[i].y, stars[i].color);
198         }
199 #ifdef SHOW_SHIELD_BAR
200 	    if ((plshield > 0) || (shieldsleft > 0)) {
201 		int shieldcount = 0;
202 		int total_shields = (plshield + shieldsleft) * 19 / SHIELDTIME + 1;
203 		while (total_shields > 0) {
204 			shieldcount++;
205 			total_shields -= 19;
206 			W_DrawImage(baseWin, WINWIDTH - 20 * shieldcount, 0, 0, shieldImage, W_Cyan);
207 		}
208 		while (total_shields < 0) {
209 			int column;
210 			column = WINWIDTH - 20 * shieldcount - total_shields++;
211 			W_MakeLine(baseWin, column, 0, column, 20, W_Black);
212 			W_MakeLine(baseWin, column - 1, 0, column - 1, 20, W_Black);
213 		}
214        	    }
215 #endif /* SHOW_SHIELD_BAR */
216 
217 
218     }
219     if(starspeed != 1) {
220         char buf[20];
221 #ifndef ORIGINAL_XGALAGA
222 	int y;
223 #endif
224 
225         drewlevel = 1;
226         sprintf(buf, "LEVEL %d", level+1);
227 #ifndef ORIGINAL_XGALAGA
228 	y = WINHEIGHT/2-W_BigTextheight/2;
229 #endif
230         W_MaskText(baseWin, WINWIDTH/2 - (W_StringWidth(buf, W_BigFont)/2), WINHEIGHT/2-W_BigTextheight/2, W_Red, buf, strlen(buf), W_BigFont);
231 #ifndef ORIGINAL_XGALAGA
232 	if (shots > 0) {
233 		int x = WINWIDTH/2 - 14 * W_Textwidth;
234 		y += W_BigTextheight + 20;
235 
236 		sprintf(buf, "Torps: %d  Hits: %d", shots, hits);
237 		W_MaskText(baseWin, x, y, W_Yellow, buf, strlen(buf), W_RegularFont);
238 
239 		x += 23 * W_Textwidth;
240 		sprintf(buf, "(%d%%)", 100 * hits / shots);
241 		W_MaskText(baseWin, x, y, W_Green, buf, strlen(buf), W_RegularFont);
242 	}
243 #endif
244     }
245 }
246 
247 /*-------------------aliens---------------*/
248 
delete_etorps()249 static void delete_etorps()
250 {
251     struct torp *tmp;
252 
253     while(first_etorp) {
254         tmp = first_etorp->next;
255         free(first_etorp);
256         first_etorp = tmp;
257     }
258 }
259 
init_aliens(int level)260 static void init_aliens(int level)
261 {
262     int i;
263 
264     convoyx=0;
265     convoymove = 1;
266 
267     maxattacking = 1 + (level*2);
268     if(maxattacking > 30)
269         maxattacking = 30;
270     attacking = 0;
271     maxetorps = 10 + (level*5);
272     numetorps = 0;
273 
274     delete_etorps();
275     metaLevel = 1;
276     if(read_level(level) <= 0)
277     {
278 	fprintf(stderr, "Error reading level %d\n", level);
279 	exit(0);
280     }
281 
282     for(i=0;i<MAXALIENS;i++) {
283 
284         livecount++;
285         new_alien(level, i, &aliens[i]);
286     }
287 
288     for(i=0;i<MAXTORPS;i++)
289         torps[i].alive = 0;
290 }
291 
292 
undo_aliens()293 static void undo_aliens()
294 {
295     int i;
296 
297     for(i=0;i<MAXALIENS;i++) {
298         if(aliens[i].alive)
299             W_ClearArea(baseWin,
300                         aliens[i].x-(aliens[i].shape->width/2),
301                         aliens[i].y-(aliens[i].shape->height/2),
302                         aliens[i].shape->width, aliens[i].shape->height);
303         if(aliens[i].dying) {
304             aliens[i].alive = 0;
305             aliens[i].dying=0;
306         }
307     }
308 }
309 
310 static void
do_escort(int i)311 do_escort(int i)
312 {
313     int fs = aliens[i].escorting;
314 
315     if(!aliens[fs].alive) {
316         aliens[i].escorting = -1;
317     } else if(aliens[fs].dir >= 0) {
318         aliens[i].dir = aliens[fs].dir;
319     } else {
320         aliens[i].x = 20 * (i - 10 * (i/10)) + convoyx + convoymove;
321         aliens[i].y = -10;
322         aliens[i].dir = -2;
323         aliens[i].path = -1;
324         aliens[i].steer = 2;
325         aliens[i].escorting = -1;
326     }
327 }
328 
329 static void
do_convoy(int i)330 do_convoy(int i)
331 {
332     aliens[i].x += convoymove;
333     if((entering == 0) &&
334        (attacking < maxattacking) &&
335        ((livecount < maxattacking) ||
336         ((random()%10000) < (level + 2 *(48-(livecount)))))) {
337         switch(random()%2) {
338         case 0:
339             path_dir(P_PEELLEFT, 0, &aliens[i].dir, &aliens[i].steer);
340             aliens[i].path = P_PEELLEFT;
341             break;
342         case 1:
343             path_dir(P_PEELRIGHT, 0, &aliens[i].dir, &aliens[i].steer);
344             aliens[i].path = P_PEELRIGHT;
345             break;
346         }
347         aliens[i].path_pos = 0;
348         attacking++;
349         if(i<10) { /* Flagship, grab escorts */
350             int e;
351             for(e=i+9;e<i+12;e++) {
352                 if(aliens[e].alive && aliens[e].dir == -1) {
353                     aliens[e].escorting = i;
354                 }
355             }
356         }
357     }
358 }
359 
new_etorp(int x,int y,int xs,int ys)360 static void new_etorp(int x, int y, int xs, int ys)
361 {
362     struct torp *t;
363 
364     t = malloc(sizeof(struct torp));
365     t->next = first_etorp;
366     if(t->next)
367         t->next->prev = t;
368     t->prev = 0;
369     first_etorp = t;
370 
371     t->x = x;
372     t->y = y;
373     t->xspeed = xs;
374     t->yspeed = ys;
375     t->alive = 1;
376     t->frame = 0;
377     numetorps++;
378 }
379 
do_enter(int i)380 static void do_enter(int i)
381 {
382     int diffx, diffy;
383     int tc;
384 
385     if(aliens[i].enterdelay) {
386         aliens[i].enterdelay--;
387         return;
388     }
389 
390     if(aliens[i].path >= 0) {
391         aliens[i].x += moves[aliens[i].dir][0] + metaLevel * moves[aliens[i].dir][0]/2;
392         aliens[i].y += moves[aliens[i].dir][1] + metaLevel * moves[aliens[i].dir][1]/2;
393 
394         aliens[i].steer--;
395         if(aliens[i].steer <= 0) {
396             aliens[i].path_pos++;
397             enter_path_dir(aliens[i].path, aliens[i].path_pos, &aliens[i].dir, &aliens[i].steer);
398 	    if(metaLevel > 1)
399 		aliens[i].steer = aliens[i].steer / (1 + ((metaLevel - 1) * .5));
400 	    /*aliens[i].steer -= ((metaLevel - 1) * (aliens[i].steer / 3));*/
401 
402             if(aliens[i].dir < 0) {
403                 aliens[i].path = -1;
404             }
405         }
406         tc = TORPCHANCE - level/2 - weapon*5;
407         if(tc < 35) tc = 35;
408         if(numetorps < maxetorps && (!(random()%tc))) {
409             int xs, ys;
410 
411             /* could aim better, not sure it should! */
412 
413             if(aliens[i].x > plx + 200) {
414                 xs = -3;
415             } else if(aliens[i].x > plx + 100) {
416                 xs = -2;
417             } else if(aliens[i].x < plx - 200) {
418                 xs = 3;
419             } else if(aliens[i].x < plx - 100) {
420                 xs = 2;
421             } else {
422                 xs = 0;
423             }
424             ys = (ETORPSPEED+level/5) - ABS(xs);
425             new_etorp(aliens[i].x, aliens[i].y, xs, ys);
426         }
427     } else {
428         diffx = ABS(convoy_x_pos(i) - aliens[i].x);
429         diffy = ABS(convoy_y_pos(i) - aliens[i].y);
430         if(diffy< 4 + (metaLevel * 2)) {
431             aliens[i].y = convoy_y_pos(i);
432             if(diffx < 4 + (metaLevel * 2)) {
433                 aliens[i].x = convoy_x_pos(i);
434                 aliens[i].dir = -1;
435                 aliens[i].entering = 0;
436                 return;
437             }
438             aliens[i].dir = 0;
439             if(convoy_x_pos(i) > aliens[i].x)
440                 aliens[i].dir = 4;
441             else
442                 aliens[i].dir = 12;
443         } else {
444 	    if(convoy_y_pos(i) < aliens[i].y) {
445 		if(diffx < 4 + (metaLevel * 2)) {
446 		    aliens[i].x = convoy_x_pos(i);
447 		    aliens[i].dir = 0;
448 		} else {
449 		    if(convoy_x_pos(i) > aliens[i].x)
450 			aliens[i].dir = 2;
451 		    else
452 			aliens[i].dir = 14;
453 		}
454 	    } else {
455 		if(diffx < 4 + (metaLevel * 2)) {
456 		    aliens[i].x = convoy_x_pos(i);
457 		    aliens[i].dir = 8;
458 		} else {
459 		    if(convoy_x_pos(i) > aliens[i].x)
460 			aliens[i].dir = 6;
461 		    else
462 			aliens[i].dir = 10;
463 		}
464 	    }
465         }
466         aliens[i].x += moves[aliens[i].dir][0] + metaLevel * moves[aliens[i].dir][0]/2;
467         aliens[i].y += moves[aliens[i].dir][1] + metaLevel * moves[aliens[i].dir][1]/2;
468     }
469 }
470 
do_aliens()471 static void do_aliens()
472 {
473     int i, j;
474     int tc;
475 
476     if(!paused) {
477         convoyx += convoymove;
478         if(convoyx <= 0) {
479             convoyx=0;
480             convoymove = -convoymove;
481         } else if(convoyx >= (WINWIDTH-180)) {
482             convoyx = WINWIDTH - 180;
483             convoymove = -convoymove;
484         }
485     }
486 
487     livecount=0; attacking = 0;
488     for(i=0, livecount=0, entering=0; i < MAXALIENS; i++) {
489         if(aliens[i].alive) {
490             livecount++;
491             if(aliens[i].dir >= 0 && aliens[i].escorting < 0 && !(aliens[i].entering))
492                 attacking++;
493             if(aliens[i].entering)
494                 entering++;
495         }
496     }
497 
498     for(i=0; i < MAXALIENS; i++) {
499         if(aliens[i].alive) {
500             if(!paused) {
501                 if(aliens[i].escorting >= 0) {
502                     do_escort(i);
503                 }
504 
505                 if(aliens[i].entering) {
506                     do_enter(i);
507                 } else if(aliens[i].dir == -1) {
508                     do_convoy(i);
509                 } else if(aliens[i].dir == -2) {
510                     aliens[i].x += convoymove;
511                     aliens[i].y+=2;
512                     if(aliens[i].y >= 20 + (20*(i/10))) {
513                         aliens[i].y = 20 + (20*(i/10));
514                         aliens[i].dir = -1;
515                     }
516                 } else {
517                     aliens[i].x += moves[aliens[i].dir][0];
518                     aliens[i].y += moves[aliens[i].dir][1];
519                     if(aliens[i].x > WINWIDTH+20)
520                         aliens[i].x = -20;
521                     else if(aliens[i].x < -20)
522                         aliens[i].x = WINWIDTH+20;
523 
524                     if(aliens[i].y > WINHEIGHT) {
525                         aliens[i].x = 20 * (i - 10 * (i/10)) + convoyx + convoymove;
526                         aliens[i].y = -30;
527                         aliens[i].dir = -2;
528                         aliens[i].path = -1;
529                         aliens[i].steer = 2;
530                         aliens[i].escorting = -1;
531                         attacking--;
532                         if(i < 10) {
533                             for(j=i+9;j<i+12;j++)
534                                 aliens[j].escorting = -1;
535                         }
536                     } else if(aliens[i].y < 0) {
537                         aliens[i].dir = 8;
538                     }
539 
540                     if(aliens[i].escorting < 0) {
541                         aliens[i].steer--;
542                         if(aliens[i].steer <= 0) {
543                             if(aliens[i].path >= 0) {
544                                 int lastdir=aliens[i].dir;
545 
546                                 aliens[i].path_pos++;
547                                 path_dir(aliens[i].path, aliens[i].path_pos, &aliens[i].dir, &aliens[i].steer);
548                                 if(aliens[i].dir < 0) {
549                                     aliens[i].dir = lastdir;
550                                 newpath:
551                                     switch(random()%8) {
552                                     case 0:
553                                         start_path(P_LOOP, &aliens[i]);
554                                         break;
555                                     case 1:
556                                         start_path(P_SWOOP1, &aliens[i]);
557                                         break;
558                                     case 2:
559                                         start_path(P_SWOOP2, &aliens[i]);
560                                         break;
561                                     case 3:
562                                         start_path(P_ZIGZAG, &aliens[i]);
563                                         break;
564                                     case 4:
565                                         start_path(P_LOOP2, &aliens[i]);
566                                         break;
567                                     case 5:
568                                         start_path(P_SPIN, &aliens[i]);
569                                         break;
570                                     case 6:
571                                         start_path(P_LEFTDIAG, &aliens[i]);
572                                         break;
573                                     case 7:
574                                         start_path(P_RIGHTDIAG, &aliens[i]);
575                                         break;
576                                     default:
577                                         aliens[i].steer = TURNSPEED;
578                                         aliens[i].path = -1;
579                                     }
580                                     if((aliens[i].path < 0) || (aliens[i].steer < 0)) {
581                                         goto newpath;
582                                     }
583                                 }
584                             } else {
585                                 if(random()%2) {
586                                     aliens[i].dir++;
587                                     if(aliens[i].dir > 15)
588                                         aliens[i].dir = 0;
589                                 } else {
590                                     aliens[i].dir--;
591                                     if(aliens[i].dir < 0)
592                                         aliens[i].dir = 15;
593                                 }
594                                 aliens[i].steer = TURNSPEED;
595                             }
596                         }
597                     }
598                     tc = TORPCHANCE - level/2 - weapon*5;
599                     if(tc < 35) tc = 35;
600 
601                     if(numetorps < maxetorps && (!(random()%tc))) {
602                         int xs, ys;
603 
604                         /* could aim better, not sure it should! */
605 
606                         if(aliens[i].x > plx + 200) {
607                             xs = -3;
608                         } else if(aliens[i].x > plx + 100) {
609                             xs = -2;
610                         } else if(aliens[i].x < plx - 200) {
611                             xs = 3;
612                         } else if(aliens[i].x < plx - 100) {
613                             xs = 2;
614                         } else {
615                             xs = 0;
616                         }
617                         ys = (ETORPSPEED+level/5) - ABS(xs);
618                         new_etorp(aliens[i].x, aliens[i].y, xs, ys);
619                     }
620                 }
621                 W_DrawImage(baseWin,
622                             aliens[i].x-(aliens[i].shape->width/2),
623                             aliens[i].y-(aliens[i].shape->height/2),
624                             aliens[i].dir < 0 ? 0 : aliens[i].dir, aliens[i].shape, W_Green);
625             } else {  /* paused */
626                 W_DrawImage(baseWin,
627                             aliens[i].x-(aliens[i].shape->width/2),
628                             aliens[i].y-(aliens[i].shape->height/2),
629                             aliens[i].dir < 0 ? 0 : aliens[i].dir, aliens[i].shape, W_Green);
630             }
631         }
632     }
633     if(livecount == 0 && !paused) {
634         starspeed++;
635 #ifdef SOUND
636         if(starspeed == 2)
637             play_sound(SND_WARP);
638 #endif
639         if(starspeed >= 120) {
640             starspeed = -20;
641         } else if(starspeed == 1) {
642             init_aliens(++level);
643             gotlemon = 0;
644             starspeed = 1;
645             numtorps=0;
646         }
647     }
648 }
649 
650 
651 /*------------------player----------------*/
init_player()652 static void init_player()
653 {
654     int i;
655     for(i=0;i<MAXTORPS;i++)
656         torps[i].alive=0;
657 }
658 
new_torp(int x,int y,int xs,int ys)659 static void new_torp(int x, int y, int xs, int ys)
660 {
661     int i;
662 
663     for(i=0;i<maxtorps;i++) {
664         if(!torps[i].alive) {
665             torps[i].x = x;
666             torps[i].y = y;
667             torps[i].alive = 1;
668             torps[i].xspeed = xs;
669             torps[i].yspeed = ys;
670             numtorps++;
671 #ifdef SOUND
672             play_sound(SND_FIRETORP);
673 #endif
674             return;
675         }
676     }
677 }
678 
undo_torps()679 static void undo_torps()
680 {
681     int i;
682 
683     for(i=0;i<MAXTORPS;i++) {
684         if(torps[i].alive) {
685             W_CacheClearArea(baseWin,
686                              torps[i].x-(playerTorp->width/2),
687                              torps[i].y-(playerTorp->height/2),
688                              playerTorp->width, playerTorp->height+1);
689         }
690     }
691 }
692 
do_torps()693 static void do_torps()
694 {
695     int i,j,k, ne;
696 
697     for(i=0;i<MAXTORPS;i++) {
698         if(torps[i].alive) {
699             if(!paused) {
700                 torps[i].y += torps[i].yspeed;
701                 torps[i].x += torps[i].xspeed;
702                 torps[i].frame++;
703                 for(j=0;j<MAXALIENS;j++) {
704                     if(aliens[j].alive && !aliens[j].dying &&
705                        (ABS(torps[i].x - aliens[j].x) < 8) &&
706                        ((ABS(torps[i].y - aliens[j].y) < 8) ||
707                         (ABS((torps[i].y + torps[i].yspeed/2)-aliens[j].y) < 8))) {
708                         aliens[j].dying = 1;
709                         if(aliens[j].dir >= 0)
710                             attacking--;
711 
712                         torps[i].alive=0;
713                         numtorps--;
714                         if(j >= 10) {
715                             if(aliens[j].dir < 0)
716                                 score += 50;
717                             else {
718                                 score += (6-(j/10))*100;
719                                 if(!(random()%(gotlemon ? 3 : PRIZECHANCE)))
720                                     new_prize(aliens[j].x, aliens[j].y);
721                             }
722                             new_explosion(aliens[j].x, aliens[j].y, 0);
723                         } else {
724                             if(aliens[j].dir < 0)
725                                 score += 200;
726                             else {
727                                 ne=0; /* count how many escorts */
728                                 for(k = j+9;k < j+12; k++) {
729                                     if(aliens[k].escorting == j)
730                                         ne++;
731                                 }
732                                 score_flagship(aliens[j].x, aliens[j].y, ne);
733                             }
734                             new_explosion(aliens[j].x, aliens[j].y, 1);
735                         }
736                         goto skip;
737                     }
738                 }
739                 if(torps[i].y < -torps[i].yspeed ||
740                    torps[i].x < ABS(torps[i].xspeed) ||
741                    torps[i].x > WINWIDTH-ABS(torps[i].xspeed)) {
742                     torps[i].alive = 0;
743                     numtorps--;
744                 } else
745                     W_DrawImage(baseWin,
746                                 torps[i].x-(playerTorp->width/2),
747                                 torps[i].y-(playerTorp->height/2),
748                                 torps[i].frame, playerTorp, W_Red);
749             skip: ;
750             } else {/* paused */
751                 W_DrawImage(baseWin,
752                             torps[i].x-(playerTorp->width/2),
753                             torps[i].y-(playerTorp->height/2),
754                             torps[i].frame, playerTorp, W_Red);
755             }
756         }
757     }
758 }
759 
do_etorps()760 static void do_etorps()
761 {
762     struct torp *t = first_etorp, *nextt;
763 
764     while(t) {
765         nextt=t->next;
766         if(t->alive) {
767             if(!paused) {
768                 t->y+=t->yspeed;
769                 t->x+=t->xspeed;
770                 t->frame++;
771                 if(t->y > WINHEIGHT || t->x < 0 || t->x > WINWIDTH) {
772                     if(t->next)
773                         t->next->prev = t->prev;
774                     if(t->prev)
775                         t->prev->next = t->next;
776                     if(t == first_etorp)
777                         first_etorp = t->next;
778                     free(t);
779                     numetorps--;
780                 } else if(!pldead && !plflash && !plshield &&
781                           (ABS(t->x - plx) < 8) &&
782                           (ABS(t->y - (WINHEIGHT - (int)playerShip->height / 2)) < 8)) { /* DEAD! */
783                     pldead = 1;
784                     new_explosion(plx, WINHEIGHT - playerShip->height/2, 2);
785                 } else {
786                     W_DrawImage(baseWin,
787                                 t->x-(enemyTorp->width/2),
788                                 t->y-(enemyTorp->height/2),
789                                 t->frame, enemyTorp, W_Red);
790                 }
791             } else {
792                 W_DrawImage(baseWin,
793                             t->x-(enemyTorp->width/2),
794                             t->y-(enemyTorp->height/2),
795                             t->frame, enemyTorp, W_Red);
796             }
797         }
798         t = nextt;
799     }
800 }
801 
undo_etorps()802 static void undo_etorps()
803 {
804     struct torp *t = first_etorp;
805 
806     while(t) {
807         W_CacheClearArea(baseWin,
808                          t->x-(enemyTorp->width/2),
809                          t->y-(enemyTorp->width/2),
810                          enemyTorp->width, enemyTorp->height);
811         t=t->next;
812     }
813 }
814 
undo_player()815 static void undo_player()
816 {
817     int y = WINHEIGHT - playerShip->height, h = playerShip->height;
818     if(plshield) {
819         y -= 3;
820         h += 3;
821     }
822     W_CacheClearArea(baseWin, plx-(playerShip->width/2), y,
823                      playerShip->width, h);
824 }
825 
do_player(mx,my,but)826 static void do_player(mx, my, but)
827 int mx, my, but;
828 {
829     static int torpok;
830 #ifdef ENABLE_MACHINE_GUN
831     static int shotside = 0;
832 #endif
833     W_Event wev;
834     static int keys = 0;
835 
836     if(gameOver) {
837         while(W_EventsPending()) {
838             W_NextEvent(&wev);
839 	    if (wev.key >= 256) wev.key -= 256;
840 
841 	    if(gameOver)
842 	      mouseControl = 1;
843 
844             switch(wev.type) {
845             case W_EV_KEY:
846                 if(score_key(&wev))
847                     continue;
848                 switch(wev.key) {
849                 case 'q':
850                 case 'Q':
851                 case 256+'Q':   //369:
852                 case 256+'q':   //337:
853                     xgal_exit(0);
854                     break;
855                 case 'm':
856                 case 256+'m':    //365:
857                     mouseControl = 2;
858                     break;
859                  case 'k':
860                  case 256+'k':   //363:
861                      mouseControl = 0;
862                      W_UngrabPointer();
863                      break;
864                 case '\r'+256:
865                     W_ToggleFullscreen(shellWin);
866                     fullscreen = !fullscreen;
867                     if (fullscreen)
868                         W_BlankCursor(baseWin);
869                     else
870                         W_RevertCursor(baseWin);
871                     return;
872                     break;
873 #ifdef SOUND
874                 case 's':       /* toggle sound on the title screen */
875                 case 256+'s':
876                     playSounds = !playSounds;
877                     return;     /* this key must not start the game */
878                     break;
879 #endif
880 		default:
881 /*printf ("1keyevent %d\n", wev.key); */
882                     return;     /* unhandled key must not cause any action */
883 		    break;
884                 }
885                 if(mouseControl < 2)
886                     mouseControl = 0;
887             case W_EV_BUTTON:
888                 if(!getting_name) {
889                     if(mouseControl)
890                         W_GrabPointer(baseWin);
891                     gameOver = 0;
892                     maxtorps = MINTORPS;
893                     weapon = 0;
894                     movespeed = MINSPEED;
895                     ships=2;
896 #ifdef ACTIVATED_SHIELD
897 		    shieldsleft = STARTSHIELDS;
898 		    shieldon = 0;
899 #else
900 		    shieldsleft = 0;
901 #endif
902                     level=startLevel;  /* change made here */
903                     init_aliens(level);
904                     gotlemon = 0;
905                     pldead = 0;
906                     score = 0;
907                     nextBonus = 20000;
908                     plx = WINWIDTH/2;
909                     W_ClearWindow(baseWin);
910                 }
911                 break;
912             case W_EV_EXPOSE:
913 	    	if (wev.Window == shellWin)
914 		    draw_score();
915 		break;
916             default:
917 /*printf ("2keyevent %d\n", wev.key);*/
918               break;
919             }
920         }
921         return;
922     }
923 
924     while(W_EventsPending()) {
925         W_NextEvent(&wev);
926 	if (wev.key >= 256) wev.key -= 256;
927 
928         switch(wev.type) {
929         case W_EV_KEY_OFF:
930             switch(wev.key) {
931             case 'f'+128:
932                 keys &= ~(RIGHTKEY);
933                 break;
934             case 'b'+128:
935                 keys &= ~(LEFTKEY);
936                 break;
937             case ' ':
938             case 256+' ':       //288:
939                 keys &= ~(FIREKEY);
940                 break;
941 #ifdef ACTIVATED_SHIELD
942 	    case 'x':
943             case 256+'x':
944 		shieldsleft += plshield;
945 		plshield = 0;
946 		shieldon = 0;
947 		break;
948 #endif
949             }
950             break;
951         case W_EV_KEY:
952             switch(wev.key) {
953             case 'f'+128:
954                 keys |= RIGHTKEY;
955                 break;
956             case 'b'+128:
957                 keys |= LEFTKEY;
958                 break;
959             case ' ':
960             case 256+' ':       //288:
961                 keys |= FIREKEY;
962                 break;
963             case 'k':
964             case 256+'k':       //363:
965                 mouseControl = 0;
966                 W_UngrabPointer();
967                 break;
968             case 'm':
969             case 256+'m':       //365:
970                 mouseControl = 1;
971                 W_GrabPointer(baseWin);
972                 break;
973 #ifdef ACTIVATED_SHIELD
974 	    case 'x':
975             case 256+'x':
976 		plshield += shieldsleft;
977 		shieldsleft = 0;
978 		shieldon = 1;
979 		break;
980 #endif
981             case 'q':
982             case 256+'q':       //369:
983                 if(!pldead  && !paused) {
984                     new_explosion(plx, WINHEIGHT - ((playerShip->height)/2), 2);
985                     ships = 0;
986                     pldead = 1;
987                 }
988                 break;
989             case 'Q':
990             case 256+'Q':       //337:
991                 xgal_exit(0);
992                 break;
993             case 'b':
994             case 256+'b':       //354:
995                 W_Buffer(baseWin, !W_IsBuffered(baseWin));
996                 W_ClearWindow(baseWin);
997                 break;
998             case 'p':
999             case 256+'p':       //368:
1000                 paused=!paused;
1001                 if(!paused) {
1002                     undo_pause();
1003                     if(mouseControl)
1004                         W_GrabPointer(baseWin);
1005                 } else
1006                     W_UngrabPointer();
1007                 break;
1008 #ifdef SOUND
1009             case 's':
1010             case 256+'s':       //371:
1011                 playSounds = !playSounds;
1012                 break;
1013 #endif
1014 #ifdef IM_A_BIG_FAT_CHEATER
1015             case 'i':
1016             case 256+'i':       //361:
1017                 if(plflash >= 0)
1018                     plflash = -2;
1019                 else
1020                     plflash = 0;
1021                 break;
1022             case 'l':
1023             case 256+'l':       //364:
1024                 {
1025                     int i;
1026                     for(i=0;i<MAXALIENS;i++)
1027                         aliens[i].alive=0;
1028                     if(starspeed != 1)
1029                         level++;
1030                 }
1031                 break;
1032             case 'c':
1033             case 256+'c':       //355:
1034                 score+= BONUSSHIPSCORE;
1035                 break;
1036             case 'h':
1037             case 256+'h':       //360:
1038                 plshield = SHIELDTIME;
1039 #ifdef SOUND
1040                 play_sound(SND_SHIELD);
1041 #endif
1042                 break;
1043             case 'w':
1044             case 256+'w':       //375:
1045                 weapon++;
1046                 if(weapon == NUMWEAPONS)
1047                     weapon=0;
1048                 break;
1049             case 't':
1050             case 256+'t':       //372:
1051                 maxtorps++;
1052                 if(maxtorps > MAXTORPS)
1053                     maxtorps = MINTORPS;
1054                 break;
1055 #endif /* IM_A_BIG_FAT_CHEATER */
1056             case '\r'+256:
1057                 W_ToggleFullscreen(shellWin);
1058                 fullscreen = !fullscreen;
1059                 if (fullscreen)
1060                     W_BlankCursor(baseWin);
1061                 else
1062                     W_RevertCursor(baseWin);
1063                 break;
1064             default:
1065 /*printf ("3keyevent %d\n", wev.key);*/
1066                 break;
1067             }
1068         case W_EV_EXPOSE:
1069 	    if (wev.Window == shellWin)
1070 	    	draw_score();
1071 	    break;
1072         }
1073     }
1074 
1075     if(!paused) {
1076         torpok--;
1077 
1078         if((!mouseControl && ! js_device) ||
1079 	   (js_device && mx == plx && but == 0)) {
1080             if(keys & LEFTKEY)
1081                 mx = 0;
1082             else if(keys & RIGHTKEY)
1083                 mx = WINWIDTH;
1084             else
1085                 mx = plx;
1086 
1087             if(keys & FIREKEY)
1088                 but = W_LBUTTON;
1089             else
1090                 but = 0;
1091         }
1092 
1093         if(pldead) {
1094             pldead++;
1095             if(pldead >= 100) {
1096                 if(ships<=0) {
1097                     gameOver = 1;
1098                     W_UngrabPointer();
1099                     if(check_score(score)) {
1100 #ifdef USE_REAL_NAMES
1101                         add_score(getUsersFullName(), score);
1102                         title_page = 1; pagetimer = 300;
1103 #else
1104                         getting_name = 1;
1105 #endif
1106                     }
1107                 } else {
1108 #ifdef DISABLE_RESET_ON_DEATH
1109                     ships--;
1110                     maxtorps--;
1111                     if (maxtorps < MINTORPS)
1112                     	maxtorps = MINTORPS;
1113                     switch (weapon)
1114                     {
1115                     	case SINGLESHOT:
1116                     		if (maxtorps < 3)
1117                     		{
1118                     			maxtorps = 3;
1119                     			weapon = SINGLESHOT;
1120                     		}
1121                     		break;
1122                     	case DOUBLESHOT:
1123 	                    	if (maxtorps < 4)
1124                     		{
1125                     			maxtorps = 4;
1126                     		}
1127         					break;
1128                    		case SPREADSHOT:
1129                     		if (maxtorps < 5)
1130                     		{
1131                     			maxtorps = 5;
1132                     		}
1133                     		break;
1134 						case TRIPLESHOT:
1135 							if (maxtorps < 6)
1136                     		{
1137                     			maxtorps = 6;
1138                     		}
1139                     		break;
1140                     	case MACHINEGUN:
1141                     		if (maxtorps < 3)
1142                     		{
1143                     			maxtorps = 3;
1144                     		}
1145                     		break;
1146 		}
1147 #else
1148                     ships--;
1149                     maxtorps = MINTORPS;
1150                     weapon = 0;
1151                     movespeed = MINSPEED;
1152 #endif /* DISABLE_RESET_ON_DEATH */
1153                     pldead = 0;
1154                     plflash = 50;
1155                     plx = WINWIDTH/2;
1156                 }
1157             }
1158             return;
1159         }
1160 
1161         if(but && torpok <= 0 && (starspeed == 1)) {
1162             switch(weapon) {
1163             case SINGLESHOT:
1164                 if(numtorps < maxtorps)
1165                     new_torp(plx, WINHEIGHT - playerShip->height, 0, -TORPSPEED);
1166 		    torpok = TORPDELAY;
1167                 break;
1168             case DOUBLESHOT:
1169                 if(numtorps < maxtorps-1) {
1170                     new_torp(plx-5, WINHEIGHT - playerShip->height, 0, -TORPSPEED);
1171                     new_torp(plx+5, WINHEIGHT - playerShip->height, 0, -TORPSPEED);
1172 		    torpok = TORPDELAY;
1173                 }
1174                 break;
1175             case TRIPLESHOT:
1176                 if(numtorps < maxtorps-2) {
1177                     new_torp(plx-5, WINHEIGHT - playerShip->height, -2, 1-TORPSPEED);
1178                     new_torp(plx,   WINHEIGHT - playerShip->height, 0,   -TORPSPEED);
1179                     new_torp(plx+5, WINHEIGHT - playerShip->height, 2, 1-TORPSPEED);
1180 		    torpok = TORPDELAY;
1181                 }
1182                 break;
1183 #ifdef ENABLE_SPREAD_SHOT
1184             case SPREADSHOT:
1185             	if (numtorps == 0)
1186                 {
1187 			if ((maxtorps % 2) == 1)
1188 				new_torp(plx, WINHEIGHT - playerShip->height, 0, -TORPSPEED*1.15);
1189 			else
1190 			{
1191 				new_torp(plx - 5, WINHEIGHT - playerShip->height, 0, -TORPSPEED*1.15);
1192 				new_torp(plx + 5, WINHEIGHT - playerShip->height, 0, -TORPSPEED*1.15);
1193 			}
1194 			if (maxtorps > 2)
1195 			{
1196 				new_torp(plx, WINHEIGHT - playerShip->height - 15, -2, -TORPSPEED*1.15);
1197 				new_torp(plx, WINHEIGHT - playerShip->height - 15, 2, -TORPSPEED*1.15);
1198 			}
1199 			if (maxtorps > 4)
1200 			{
1201 				new_torp(plx, WINHEIGHT - playerShip->height - 25, -4, -TORPSPEED*1.15);
1202 				new_torp(plx, WINHEIGHT - playerShip->height - 25, 4, -TORPSPEED*1.15);
1203 			}
1204 			if (maxtorps > 6)
1205 			{
1206 				new_torp(plx, WINHEIGHT - playerShip->height - 35, -6, -TORPSPEED*1.15);
1207 				new_torp(plx, WINHEIGHT - playerShip->height - 35, 6, -TORPSPEED*1.15);
1208 			}
1209 			if (maxtorps > 8)
1210 			{
1211 				new_torp(plx, WINHEIGHT - playerShip->height - 50, -8, -TORPSPEED*1.15);
1212 				new_torp(plx, WINHEIGHT - playerShip->height - 50, 8, -TORPSPEED*1.15);
1213 			}
1214 			if (maxtorps > 10)
1215 			{
1216 				new_torp(plx, WINHEIGHT - playerShip->height - 60, -10, -TORPSPEED*1.15);
1217 				new_torp(plx, WINHEIGHT - playerShip->height - 60, 10, -TORPSPEED*1.15);
1218                         }
1219                         torpok = TORPDELAY;
1220                  }
1221 		 break;
1222 #endif /* ENABLE_SPREAD_SHOT */
1223 #ifdef ENABLE_MACHINE_GUN
1224             case MACHINEGUN:
1225                  if(numtorps < maxtorps)
1226                  {
1227 			shotside = (shotside == -15) ? 15 : -15;
1228 			new_torp(plx + shotside, WINHEIGHT - playerShip->height, 0, -TORPSPEED * 1.3);
1229 			torpok = TORPDELAY - 2;
1230 		}
1231 		break;
1232 #endif /* ENABLE_MACHINE_GUN */
1233 	    }
1234 	}
1235 
1236 
1237         if(!but)
1238             torpok=0;
1239 
1240         if((mx/movespeed) > (plx/movespeed))
1241             plx+=movespeed;
1242         else if((mx/movespeed) < (plx/movespeed))
1243             plx-=movespeed;
1244 #ifdef ENABLE_SHIP_WRAP
1245 
1246         if(plx < 10)
1247         	plx=WINWIDTH - 10;
1248         if(plx > WINWIDTH - 10)
1249 			plx=10;
1250 #else
1251 
1252 
1253         if(plx < playerShip->width/2)
1254             plx=playerShip->width/2;
1255         if(plx> WINWIDTH - playerShip->width/2)
1256             plx=WINWIDTH - playerShip->width/2;
1257 #endif
1258 
1259         if(plflash > 0)
1260             plflash--;
1261         if(!(plflash % 2))
1262             W_DrawImage(baseWin, plx-(playerShip->width/2), WINHEIGHT - playerShip->height, counter, playerShip, W_Red);
1263         if(plshield > 0)
1264             plshield--;
1265         if(plshield && ((plshield > SHIELDTIME/4) || plshield % 2)) {
1266             W_DrawImage(baseWin, plx-(shieldImage->width/2), WINHEIGHT - shieldImage->height - 3, 0, shieldImage,
1267                         W_Cyan);
1268         }
1269     } else if (!pldead) { /* paused */
1270         W_DrawImage(baseWin, plx-(playerShip->width/2), WINHEIGHT - playerShip->height, counter, playerShip, W_Red);
1271     }
1272 }
1273 
1274 int
main(argc,argv)1275 main(argc, argv)
1276 int     argc;
1277 char  **argv;
1278 {
1279     int ac;
1280     char *dpyname = 0;
1281     int mx, my, but;
1282     int start_fullscreen = 1;
1283 
1284     for(ac = 1; ac < argc; ac++) {
1285         if(*argv[ac] == '-') {
1286             int w, h;
1287             if(strcmp(argv[ac], "-scores") == 0) {
1288                 print_scores();
1289                 exit(0);
1290             }
1291             if(strcmp(argv[ac], "-display") == 0 && (ac+1 < argc)) {
1292                 dpyname = argv[ac+1];
1293                 ac++;
1294             } else if (strcmp(argv[ac], "-b") == 0) {
1295                 useBuffered = !useBuffered;
1296             } else if (strcmp(argv[ac], "-keyboard") == 0) {
1297                 mouseControl = 0;
1298             } else if (strcmp(argv[ac], "-mouse") == 0) {
1299                 mouseControl = 1;
1300 #ifdef SOUND
1301             } else if (strcmp(argv[ac], "-nosound") == 0) {
1302                 playSounds = 0;
1303 #endif
1304 	/* '-level' option defined here */
1305             } else if (strcmp(argv[ac], "-level") == 0 && (ac+1 < argc)
1306                        && atoi(argv[ac+1]) >= 1)
1307             {
1308                 int nlev;
1309                 nlev = atoi(argv[ac+1]);
1310                 if (nlev > 15 ) nlev = 15;
1311                 startLevel = nlev;
1312                 ac++;
1313             } else if (strcmp(argv[ac], "-nostars") == 0) {
1314                 wantStars = 0;
1315             } else if (strcmp(argv[ac], "-nouseor") == 0) {
1316                 useOR = 0;
1317             } else if (strcmp(argv[ac], "-noclipmask") == 0) {
1318                 useClipMask = 0;
1319             } else if (strcmp(argv[ac], "-window") == 0) {
1320                 start_fullscreen = 0;
1321             } else if ((strcmp(argv[ac], "-winsize") == 0) && (++ac < argc) &&
1322                        (sscanf(argv[ac], "%dx%d", &w, &h) == 2)) {
1323                 WINWIDTH  = w;
1324                 WINHEIGHT = h;
1325             } else {
1326                 print_usage();
1327                 exit(0);
1328             }
1329         } else {
1330             print_usage();
1331             exit(0);
1332         }
1333     }
1334 
1335     W_Initialize(dpyname);
1336 
1337     backColor = W_Black;
1338     WINHEIGHT -= (W_Textheight+1);
1339     shellWin = W_MakeWindow("XGalaga", 0, 0, WINWIDTH, WINHEIGHT + W_Textheight+1, 0, "tiny", 0, W_White);
1340     baseWin = W_MakeWindow("", 0, W_Textheight+1, WINWIDTH, WINHEIGHT, shellWin, "tiny", 0, W_White);
1341     W_Buffer(shellWin, 0);
1342     W_MapWindow(shellWin);
1343     W_MapWindow(baseWin);
1344     if (start_fullscreen)
1345     {
1346       W_ToggleFullscreen(shellWin);
1347       W_BlankCursor(baseWin);
1348       fullscreen = 1;
1349     }
1350 
1351     /*    W_AutoRepeatOff();*/
1352 
1353     W_Flush();
1354 
1355     W_SetImageDir(IMAGEDIR);
1356 
1357     playerShip = getImage(I_PLAYER1);
1358     playerTorp = getImage(I_MTORP);
1359     enemyTorp = getImage(I_ETORP);
1360     shieldImage = getImage(I_SHIELD);
1361 
1362     level=startLevel;   /* change made here */
1363 
1364     load_scores();
1365     init_titles();
1366     init_player();
1367     init_stars();
1368     init_explosions();
1369     init_score();
1370     init_prizes();
1371 #ifdef SOUND
1372     init_sound();       /* starts the soundserver */
1373 #endif
1374     init_aliens(level);
1375     init_framerate();
1376 #ifdef __linux__
1377     init_joystick();
1378 #endif
1379 
1380     ships = 2;
1381     nextBonus = 20000;
1382 
1383     while(1) {
1384         counter++;
1385 
1386 	/* For the benefit of unbuffered mode, the most important things are
1387 	 * erased/redrawn closest together so they spend the least time blanked.
1388 	 * player, aliens and etorps are most important for game play.
1389 	 * pause, title and name are important in their modes and aren't done
1390 	 * otherwise.
1391 	 *
1392 	 * The title, name, pause and score "extra ship" want to overlay
1393 	 * everything else drawn, so they come last.
1394 	 */
1395 	undo_stars();
1396         undo_explosions();
1397         undo_prizes();
1398         undo_torps();
1399         undo_etorps();
1400         undo_aliens();
1401         undo_player();
1402         if(gameOver && getting_name) undo_name();
1403         if(paused) undo_pause();
1404         undo_score();
1405 
1406         W_FlushClearAreaCache(baseWin);
1407 
1408         do_etorps();
1409         do_player(mx, my, but);
1410         do_aliens();
1411         do_torps();
1412         do_prizes();
1413         do_explosions();
1414  	do_stars();
1415         do_score();
1416         if(gameOver) { do_title(); if(getting_name) do_name(); }
1417         if(paused) do_pause();
1418 
1419         W_DisplayBuffer(baseWin);
1420 
1421 	/* This is an XSync style round trip to the server with the bonus of
1422 	 * getting the mouse position.
1423 	 * If the server can't draw at the UTIMER frame rate then this will be
1424 	 * the only delay in the loop.
1425 	 */
1426 	W_GetMouse(baseWin, &mx, &my, &but);
1427 #ifdef __linux__
1428 	do_joystick(&mx, &my, &but);
1429 #endif
1430         do_framerate();
1431 
1432     }
1433     return (0);
1434 }
1435 
1436