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