1 /*
2 **
3 ** XOIDS - main module
4 **
5 ** v1.5 by Tim Ebling
6 **
7 ** tebling@oce.orst.edu
8 **
9 ** OIDS.C
10 **
11 ** September, 1996
12 */
13
14 #include <stdio.h>
15 #include <math.h>
16 #include <stdlib.h>
17 #include <time.h>
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20
21 #include "oids.h"
22 #include "bitmaps.h"
23
main()24 main()
25 {
26 int i;
27 Sprite *the_obj;
28
29 /* Build the trig tables */
30
31 for (i=0;i<MAPS_PER_360;i++) {
32 cos_table[i] = cos(i*(2*PI)/MAPS_PER_360);
33 sin_table[i] = sin(i*(2*PI)/MAPS_PER_360);
34 }
35
36 /* Reset all our globals */
37
38 keyboard_state = 0;
39 timer = TIMER_VAL;
40 delay = 0;
41 num_bursts = 0;
42 extra_man_scr = 0;
43 oids_shot = 0;
44 game_over = 0;
45 goal_score = 1;
46 warp_levels = 0;
47 leave = 0;
48 level = 1;
49
50 /* Set the window size to the default values */
51
52 vdevice.sizeSx = WINDOW_WIDTH;
53 vdevice.sizeSy = WINDOW_HEIGHT;
54
55 init_all_objects(0);
56
57 X_init();
58 X_clear();
59
60 X_init_pixmaps();
61
62 /* Main game loop - leaving this loop exits the program */
63
64 while (1) {
65
66 X_clear_key_buffer();
67
68 /* Call forth the main game menu */
69
70 main_menu();
71
72 X_backbuf();
73 X_clear();
74 update_score();
75
76 the_obj = P;
77
78 /* Bring the players to life */
79
80 do {
81 revive_player(the_obj);
82 } while (the_obj = the_obj->next);
83
84 /* PLAY LOOP */
85
86 while(game_over < 100 || !leave) {
87
88 /* Draw stuff */
89
90 /* handle player(s) first */
91
92 the_obj = P;
93
94 do {
95 X_draw_object(the_obj);
96 if (the_obj->thrust) X_draw_thrust(the_obj);
97 switch(the_obj->state) {
98 case WARPING:
99 if (the_obj->state_ctr == HYPER_DELAY) warp_object(the_obj);
100 break;
101 case DEAD:
102 if (!game_over) revive_player(the_obj);
103 break;
104 }
105
106 } while (the_obj = the_obj->next);
107
108 /* Handle end of one player game */
109
110 if (num_players == 1 && P->lives == 0) {
111 game_over = 1;
112 P->lives = -1;
113 }
114
115 /* Check for bonus life in the one player game */
116
117 if (num_players == 1 && (P->score - extra_man_scr) >= 10000) {
118 if (++P->lives > 20) P->lives = 20;
119 extra_man_scr += 10000;
120 update_score();
121 make_burst(P, -1);
122 }
123
124 /* Handle end of two player game */
125
126 if (num_players > 1 && !game_over && (P->score >= goal_array[goal_score] || P->next->score >= goal_array[goal_score])) {
127 game_over = 1;
128 }
129
130 /* Drawing to offscreen pixmap - could clean all this */
131 /* stuff up with some a list of all the game objects */
132 /* (C++ would come in handy here) */
133
134 X_draw_object(Big_O);
135 X_draw_shots(P);
136 X_draw_object(Slrb);
137 X_draw_shots(Slrb);
138 X_draw_object(Homer);
139 X_draw_object(Resur);
140 if (!Resur->state && Resur->rotation) {
141 Flame->curr_map = ((int) random_num(0.0, 6.0)) % 2;
142 Flame->x = Resur->x;
143 Flame->y = Resur->y;
144 X_draw_object(Flame);
145 }
146 X_draw_object(Power_Up);
147 X_draw_object(Meta);
148 X_update_status_bar(); /* MUST follow all drawing */
149
150 /* Copy from offscreen pixmap to window */
151
152 X_copy_object_to_window(P);
153 X_copy_object_to_window(Slrb);
154 X_copy_object_to_window(Homer);
155 X_copy_object_to_window(Resur);
156 X_copy_object_to_window(Power_Up);
157 X_copy_object_to_window(Meta);
158 X_copy_status_bar_to_window();
159 X_copy_object_to_window(Big_O); /* Leave this to end */
160
161 X_update(1);
162
163 /* Drawing to the window */
164 /* Update bursts */
165
166 X_draw_bursts(0);
167 update_bursts();
168 X_draw_bursts(1);
169
170 X_draw_stars(WHITE, 0);
171
172 if (coop) X_draw_link(P, GREEN);
173
174 /* Get stuff from keyboard, mouse, etc... */
175
176 if (!game_over) check_input();
177
178 /* Update sprite positions */
179
180 update_object(P);
181 if (coop) link_players();
182 update_object(Slrb);
183 update_object(Homer);
184 update_object(Resur);
185 update_object(Big_O);
186 update_object(Power_Up);
187 update_object(Meta);
188
189 /* Clear old positions */
190
191 X_clear_object(P);
192 X_clear_object(Slrb);
193 X_clear_object(Homer);
194 X_clear_object(Resur);
195 X_clear_object(Power_Up);
196 X_clear_object(Meta);
197 X_clear_object(Big_O);
198
199 /* Update shot positions */
200
201 if (the_obj = update_shots(P)) {
202 if (!(coop && (the_obj == P || the_obj == P->next)))
203 (*the_obj->kill_func)(the_obj);
204 }
205
206 if (the_obj = update_shots(Slrb)) {
207 (*the_obj->kill_func)(the_obj);
208 }
209
210 X_clear_shots(P);
211 X_clear_shots(Slrb);
212 if (shot_clock) shot_clock--;
213
214 /* Check for collisions (ooh, aahh) */
215
216 /* Player collisions */
217
218 check_collision(P, Big_O, 1, 1);
219
220 if (num_players > 1) simple_collision(P, P->next, 1, 1);
221
222 /* Alien collisions */
223
224 if (Slrb->state <= DYING) {
225 check_collision(Slrb, Big_O, 1, 1);
226 simple_collision(P, Slrb, 1, 1);
227 if (num_players > 1) simple_collision(P->next, Slrb, 1, 1);
228 if (Slrb->wpn != GUN && Slrb->shots < Weapon[Slrb->wpn].max_shots && !shot_clock) create_new_shot(Slrb);
229
230 }
231
232 if (!Homer->state) {
233 alien_lock_on(Homer);
234 draw_homer_trail(0);
235 check_collision(Homer, Big_O, 1, 1);
236 simple_collision(P, Homer, 1, 1);
237 if (!Slrb->state) simple_collision(Homer, Slrb, 1, 1);
238 if (num_players > 1) simple_collision(P->next, Homer, 1, 1);
239 }
240
241 if (!Resur->state) {
242 check_collision(Resur, Big_O, 1, 0);
243 simple_collision(P, Resur, 1, 1);
244 if (!Slrb->state) simple_collision(Resur, Slrb, 1, 1);
245 if (!Homer->state) simple_collision(Resur, Homer, 1, 1);
246 if (num_players > 1) simple_collision(P->next, Resur, 1, 1);
247 }
248
249 if (!Power_Up->state) {
250 the_obj = NULL;
251 check_collision(Power_Up, Big_O, 1, 1);
252 if (simple_collision(P, Power_Up, 0, 0)) the_obj = P;
253 if (simple_collision(Slrb, Power_Up, 0, 0)) the_obj = Slrb;
254 if (num_players > 1 && simple_collision(P->next, Power_Up, 0, 0)) the_obj = P->next;
255
256 if (the_obj) kill_power_up(Power_Up, the_obj);
257 }
258
259 if (!Meta->state) {
260 the_obj = NULL;
261 check_collision(Meta, Big_O, 1, 1);
262 if (simple_collision(P, Meta, 0, 0)) the_obj = P;
263 if (simple_collision(Slrb, Meta, 0, 0)) the_obj = Slrb;
264 if (num_players > 1 && simple_collision(P->next, Meta, 0, 0)) the_obj = P->next;
265
266 if (the_obj) kill_power_up(Meta, the_obj);
267 }
268
269 /* Update timer (used by oid rotation) */
270
271 if (timer) timer--;
272 if (!timer) {
273 test_for_aliens();
274 timer = TIMER_VAL;
275 }
276
277 /* Delay loop if running too fast */
278
279 if (delay) do_nothing_for_a_while();
280
281 /* Finished the level, so warp outta here */
282
283 if (warp_levels) {
284 if (warp_levels == MAPS_PER_360) X_draw_stars(WHITE, 1);
285 warp_levels--;
286 draw_level_warp();
287 if (!warp_levels) {
288 num_bursts = 0;
289 X_flash_screen(2);
290 }
291 }
292
293 /* If game is over, wait for the end */
294
295 if (game_over) {
296 game_over++;
297 if (!leave) {
298 leave = X_checkkey();
299 draw_game_over();
300 }
301 if (leave == (char) 'q') {
302 X_exit();
303 exit(0);
304 }
305 }
306
307 }
308
309 /* Game has ended, so reset the state of everything */
310
311 X_backbuf();
312 X_clear();
313 X_frontbuf();
314 X_clear();
315
316 init_all_objects(1);
317 level = 1;
318
319 game_over = leave = 0;
320 oids_shot = 0;
321 extra_man_scr = 0;
322 P->wpn = GUN;
323 if (P->next) P->next->wpn = GUN;
324 keyboard_state = 0;
325
326 }
327
328 } /* end OIDS.C */
329
330
331
332 /*
333 ** CHECK_INPUT
334 **
335 ** Handle keyboard state.
336 */
check_input()337 void check_input()
338 {
339
340 Sprite *player;
341 float diff_ang;
342
343 X_check_keypress();
344
345 /* PLAYER ONE CONTROLS */
346
347 player = P;
348
349 if (!player->state) {
350
351 /* THRUST */
352
353 if (keyboard_state & KEY_THRUST1) {
354 if (coop) {
355 if (!player->next->state) {
356
357 /* Here's where the neat link dynamics are */
358
359 diff_ang = link.ang - player->curr_map * DELTA_ANG;
360 link.ang_vec += 0.01 * sin(diff_ang);
361
362 link.length -= (int) (5.0 * cos(diff_ang));
363
364 link.x_vec -= fabs(cos(diff_ang)) * sin_table[player->curr_map];
365 link.y_vec -= fabs(cos(diff_ang)) * cos_table[player->curr_map];
366 } else {
367 player->y_vec += -cos_table[player->curr_map];
368 player->x_vec += -sin_table[player->curr_map];
369 }
370 } else {
371 player->y_vec += -cos_table[player->curr_map];
372 player->x_vec += -sin_table[player->curr_map];
373 }
374 player->thrust = 1;
375 player->engine += 4;
376 } else {
377 player->thrust = 0;
378 }
379
380 if (keyboard_state & KEY_LEFT1) { /* ROTATE LEFT */
381 player->point_angle += player->delta_angle;
382 player->curr_map = (player->curr_map + 1) % player->num_pixmaps;
383 }
384
385 if (keyboard_state & KEY_RIGHT1) { /* ROTATE RIGHT */
386 player->point_angle += player->delta_angle;
387 player->curr_map = (player->curr_map - 1) % player->num_pixmaps;
388 }
389 /* FIRE */
390
391 if (keyboard_state & KEY_FIRE1 && player->shots < Weapon[player->wpn].max_shots && !shot_clock) {
392 create_new_shot(player);
393 shot_clock = Weapon[player->wpn].inhibit_time;
394 }
395
396 if (keyboard_state & KEY_HYPER1 && !coop) { /* HYPERSPACE */
397 player->state = NORMAL;
398 X_clear_one_object(player);
399 X_copy_object_to_window(player);
400 player->state = WARPING;
401 keyboard_state = keyboard_state ^ KEY_HYPER1;
402 player->engine += 300;
403 }
404 }
405
406 /* PLAYER TWO CONTROLS */
407
408 if (player = P->next) {
409 if (!player->state) {
410 if (keyboard_state & KEY_THRUST2) {
411
412 /* Here's where the neat link dynamics are */
413
414 if (coop && !P->state) {
415 diff_ang = link.ang - player->curr_map * DELTA_ANG;
416 link.ang_vec -= 0.01 * sin(diff_ang);
417 link.length += (int) (5.0 * cos(diff_ang));
418 link.x_vec -= fabs(cos(diff_ang)) * sin_table[player->curr_map];
419 link.y_vec -= fabs(cos(diff_ang)) * cos_table[player->curr_map];
420 } else {
421 player->y_vec += -cos_table[player->curr_map];
422 player->x_vec += -sin_table[player->curr_map];
423 }
424 player->thrust = 1;
425 player->engine += 4;
426 } else {
427 player->thrust = 0;
428 }
429
430 if (keyboard_state & KEY_LEFT2) { /* ROTATE LEFT */
431 player->point_angle += player->delta_angle;
432 player->curr_map = (player->curr_map + 1) % player->num_pixmaps;
433 }
434
435 if (keyboard_state & KEY_RIGHT2) { /* ROTATE RIGHT */
436 player->point_angle += player->delta_angle;
437 player->curr_map = (player->curr_map - 1) % player->num_pixmaps;
438 }
439 /* FIRE */
440
441 if (keyboard_state & KEY_FIRE2 && player->shots < Weapon[player->wpn].max_shots && !shot_clock) {
442 create_new_shot(player);
443 shot_clock = Weapon[player->wpn].inhibit_time;
444 }
445
446 if (keyboard_state & KEY_HYPER2 && !coop) { /* HYPERSPACE */
447 player->state = NORMAL;
448 X_clear_object(player);
449 X_copy_object_to_window(player);
450 player->state = WARPING;
451 keyboard_state = keyboard_state ^ KEY_HYPER2;
452 player->engine += 300;
453 }
454 }
455
456 }
457
458 if (keyboard_state & KEY_QUIT) { /* QUIT */
459 X_exit();
460 exit(0);
461 }
462
463 if (keyboard_state & KEY_PAUSE) { /* PAUSE */
464 pause_game();
465 keyboard_state = 0;
466 }
467
468 if (keyboard_state & KEY_ESC) { /* ESCAPE */
469 leave = 1;
470 game_over = 100;
471 }
472
473 } /* end CHECK_INPUT */
474
475
476
477 /*
478 ** UPDATE_OBJECT
479 **
480 ** Called each game loop - should make this routine FAST
481 **
482 */
update_object(obj)483 void update_object(obj)
484 Sprite *obj;
485 {
486
487 do {
488
489 switch (obj->state) {
490
491 case WARPING:
492 obj->state_ctr++;
493 break;
494
495 case REVIVING:
496 if (obj->death_sprite) {
497 update_object(obj->death_sprite);
498 if (fabs(obj->death_sprite->x - obj->x) < 2) {
499 X_clear_object(obj->death_sprite);
500 X_copy_object_to_window(obj->death_sprite);
501 obj->state = NORMAL;
502 obj->state_ctr = 0;
503 }
504 }
505 break;
506
507 case EXPLODING:
508 if (++obj->state_ctr > obj->max_exploding) {
509 X_clear_one_object(obj);
510 obj->state = DEAD; /* Toasted */
511 obj->wpn = GUN;
512 }
513 if (obj->death_sprite) {
514 update_object(obj->death_sprite);
515 }
516 break;
517
518 case DEAD:
519 break;
520
521 case DYING:
522 if (++obj->state_ctr > obj->max_dying) {
523 (*obj->kill_func)(obj);
524 }
525
526 /* NOTE - fallthrough to default from DYING */
527
528 default:
529
530 obj->ox = obj->x;
531 obj->oy = obj->y;
532
533 if (fabs(obj->x_vec) > obj->max_speed) obj->x_vec = fabs(obj->x_vec)/obj->x_vec*obj->max_speed;
534 if (fabs(obj->y_vec) > obj->max_speed) obj->y_vec = fabs(obj->y_vec)/obj->y_vec*obj->max_speed;
535
536 obj->x += obj->x_vec;
537
538 obj->y += obj->y_vec;
539
540 put_inside_window(obj);
541
542 if (obj->rotation && (timer % obj->rotation) == 0) {
543 obj->curr_map = (obj->curr_map + obj->rot_dir) % obj->num_pixmaps;
544 }
545
546 break;
547
548 }
549
550 if (obj->bounced) obj->bounced--;
551 if (obj->engine) {
552 if (obj->engine > 999 && obj->state != EXPLODING) {
553 (*obj->kill_func)(obj);
554 } else {
555 obj->engine--;
556 }
557 }
558 if (obj->state_ctr > 1000) obj->state_ctr = 1;
559
560 } while (obj = obj->next);
561
562
563 } /* end UPDATE_OBJECT */
564
565
566
567 /*
568 ** LINK_PLAYERS
569 **
570 ** Deal with the nasty physics of two players linked together
571 ** via that elastic space-cable. Actually, most of that is found
572 ** in check_input().
573 */
link_players()574 void link_players()
575 {
576
577 int stretch;
578
579 stretch = (int) (LINK_LENGTH - link.length) / 15;
580
581 /* Differential stretching */
582
583 if (link.length != LINK_LENGTH) link.length += stretch;
584
585 if (!(P->state || P->next->state)) {
586
587 /* Minimum length of link */
588
589 if (link.length < P->height) link.length = P->height;
590
591 /* Maximum angular velocity of link */
592
593 if (fabs(link.ang_vec) > 0.0025 * link.length) link.ang_vec = 0.0025 * link.length * fabs(link.ang_vec) / link.ang_vec;
594
595 link.ang += link.ang_vec;
596
597 /* Maximum translational velocity of link */
598
599 if (fabs(link.x_vec) > P->max_speed) link.x_vec = fabs(link.x_vec)/link.x_vec*P->max_speed;
600 if (fabs(link.y_vec) > P->max_speed) link.y_vec = fabs(link.y_vec)/link.y_vec*P->max_speed;
601
602
603 link.y += link.y_vec;
604 link.x += link.x_vec;
605
606 /* Handle window borders */
607
608 if (link.x > vdevice.sizeSx) link.x = 0;
609 if (link.x < 0) link.x = vdevice.sizeSx;
610
611 if (link.y > vdevice.sizeSy) link.y = P->height + 3;
612 if (link.y < P->height + 3) link.y = vdevice.sizeSy;
613
614 /* Put the players at the ends of the link */
615
616 P->x = (int) link.length * sin(link.ang) + link.x;
617 P->y = (int) link.length * cos(link.ang) + link.y;
618 P->x_vec = P->x - P->ox;
619 P->y_vec = P->y - P->oy;
620
621 P->next->x = link.x - (int) link.length * sin(link.ang);
622 P->next->y = link.y - (int) link.length * cos(link.ang);
623 P->next->x_vec = P->next->x - P->next->ox;
624 P->next->y_vec = P->next->y - P->next->oy;
625
626 }
627
628 if (!(P->state && P->next->state)) link.ang += link.ang_vec;
629
630 } /* end LINK_PLAYERS */
631
632
633
634
635 /*
636 ** TEST_FOR_ALIENS
637 **
638 ** Check to see if an alien has appeared. Also handle some (most)
639 ** of the alien logic.
640 */
test_for_aliens()641 void test_for_aliens()
642 {
643
644 float z, ang;
645 Sprite *lock_on;
646 int s, i, j, fl, max_oids, map;
647 static int resurr_state, res_map = 2, wait = 115;
648
649 z = random_num(0.0, 20.0);
650
651 max_oids = INIT_BIG_OIDS + floor(((float)level - 1.0) / 2.0);
652
653 /* Slurb */
654
655 if (z < 1.0 && Slrb->state == DEAD) {
656 revive_player(Slrb);
657
658 } else if (Slrb->state == NORMAL && (z > 11.0 || Slrb->x_vec == 0.0)) {
659 alien_lock_on(Slrb);
660 }
661
662 if (!Slrb->state && z < 17.0) {
663 create_new_shot(Slrb);
664 }
665
666 /* Homer */
667
668 if (z > 10.0 && z < (9.0 + ((level < 10) ? level / 2.0 : 5.0)) && Homer->state == DEAD) {
669
670 /* Want Homer to appear from the edge of the window */
671
672 s = (int) random_num(0.0, 4.0);
673 switch (s) {
674 case 0:
675 Homer->x = 0;
676 Homer->y = (int) random_num(P->height, (float) vdevice.sizeSy);
677 break;
678 case 1:
679 Homer->x = vdevice.sizeSx - 1;
680 Homer->y = (int) random_num(P->height, (float) vdevice.sizeSy);
681 break;
682 case 2:
683 Homer->x = (int) random_num(0.0, (float) vdevice.sizeSx);
684 Homer->y = 0;
685 break;
686 case 3:
687 Homer->x = (int) random_num(0.0, (float) vdevice.sizeSx);
688 Homer->y = vdevice.sizeSy - 1;
689 break;
690 default:
691 Homer->x = (int) random_num(0.0, (float) vdevice.sizeSx);
692 Homer->y = (int) random_num(0.0, (float) vdevice.sizeSy);
693 break;
694 }
695
696 draw_homer_trail(1);
697 Homer->state = NORMAL;
698
699 }
700
701 /* Resurrector */
702
703 if ((z = random_num(0.0, 20.0)) > 23.0 - level / 2.0 && oids_shot > 15 && Resur->state == DEAD) {
704 revive_player(Resur);
705
706 Resur->x_vec = random_num(-4.0, 4.0);
707 Resur->y_vec = random_num(-4.0, 4.0);
708
709 ang = atan2(Resur->x_vec, Resur->y_vec);
710
711 map = (int) ((ang + PI) / (2*PI) * MAPS_PER_360);
712
713 Resur->curr_map = map;
714
715 resurr_state = 1;
716 }
717
718 /* Sit and spin */
719
720 if ((random_num(0.0, 20.0) < 10.0 && resurr_state == 1) || resurr_state == 3) {
721
722 if (res_map == 2) {
723 Resur->x_vec = 0.0;
724 Resur->y_vec = 0.0;
725 }
726
727 if (!--res_map) {
728 res_map = 2;
729 resurr_state++;
730 Resur->rotation = 0;
731 } else {
732 Resur->rotation = 1;
733 }
734 }
735
736 /* Resurrector does his stuff - revive a dead oid! */
737
738 if (resurr_state == 5) resurr_state = 4;
739
740 if (resurr_state == 2) {
741
742 fl = 0;
743
744 for (i=0;i<max_oids * OID_DIVIDE;i++) {
745 lock_on = Sml_O[i];
746 while (lock_on && lock_on->state != DEAD) {
747 lock_on = lock_on->next_draw;
748 }
749 if (lock_on && !fl && !Resur->state) {
750 lock_on->x_vec = - (float) SO_MAX_SPEED*sin_table[Resur->curr_map];
751 lock_on->y_vec = - (float) SO_MAX_SPEED*cos_table[Resur->curr_map];
752 lock_on->state = NORMAL;
753 lock_on->x = Resur->x + 3.0*lock_on->x_vec;
754 lock_on->y = Resur->y + 3.0*lock_on->y_vec;
755 oids_shot--;
756
757 Resur->x_vec = 0.0;
758 Resur->y_vec = 0.0;
759 Resur->rotation = 0;
760
761 fl = 1;
762 };
763 }
764
765 if (fl) {
766 resurr_state = 5;
767 } else {
768 resurr_state = 4;
769 }
770 }
771
772 if (resurr_state == 4) {
773
774 Resur->x_vec = - (float) P_MAX_SPEED * sin_table[Resur->curr_map];
775 Resur->y_vec = - (float) P_MAX_SPEED * cos_table[Resur->curr_map];
776
777 resurr_state = 1;
778
779 }
780
781
782 } /* end TEST_FOR_ALIENS */
783
784
785 /*
786 ** DRAW_HOMER_TRAIL
787 **
788 ** It's a well known fact that Homers leave trails of red
789 ** space-slime behind them. Duh.
790 */
draw_homer_trail(reset)791 void draw_homer_trail(reset)
792 int reset;
793 {
794
795 int i;
796 static int homer_trail[20][2], c = 1;
797
798 if (reset == 2) {
799 X_color(BLACK);
800 for (i=0;i<20;i++)
801 X_point(homer_trail[i][0], homer_trail[i][1]);
802
803 } else if (!(--c)) {
804
805 c = 1;
806
807 if (reset == 1)
808 for (i=0;i<20;i++) {
809 homer_trail[i][0] = (int) Homer->x;
810 homer_trail[i][1] = (int) Homer->y;
811 }
812
813 X_color(BLACK);
814
815 X_point(homer_trail[0][0], homer_trail[0][1]);
816
817 X_color(RED);
818
819 for (i=0;i<19;i++) {
820 homer_trail[i][0] = homer_trail[i+1][0];
821 homer_trail[i][1] = homer_trail[i+1][1];
822 if (i) X_point(homer_trail[i][0], homer_trail[i][1]);
823 }
824
825 homer_trail[19][0] = (int) Homer->x;
826 homer_trail[19][1] = (int) Homer->y;
827
828 X_color(BLACK);
829 }
830
831 }
832
833
834 /*
835 ** ALIEN_LOCK_ON
836 **
837 ** Give the alien a purpose in life.
838 */
alien_lock_on(alien)839 void alien_lock_on(alien)
840 Sprite *alien;
841 {
842
843 Sprite *lock_on = NULL;
844 int map;
845 float ang, s;
846
847 if (P->pixmaps == P->orig_pixmaps) lock_on = P;
848
849 if (num_players > 1) {
850 if (distance(alien, P->next) < distance(alien, P) && P->next->pixmaps == P->next->orig_pixmaps)
851 lock_on = P->next;
852 }
853
854 if (!Slrb->state && alien == Homer) lock_on = Slrb;
855
856 if (alien == Slrb && !Power_Up->state) lock_on = Power_Up;
857
858 if (!lock_on) {
859
860 alien->x_vec = (int) (random_num(-4.0, 4.0));
861 alien->y_vec = (int) (random_num(-4.0, 4.0));
862
863 } else if (!lock_on->state) {
864
865 alien->x_vec = (lock_on->x - alien->x);
866 alien->y_vec = (lock_on->y - alien->y);
867
868 s = speed(alien);
869
870 alien->x_vec = alien->max_speed * alien->x_vec / s;
871 alien->y_vec = alien->max_speed * alien->y_vec / s;
872
873 ang = atan2(alien->x_vec, alien->y_vec);
874
875 map = (int) ((ang + PI) / (2*PI) * MAPS_PER_360);
876
877 alien->curr_map = map;
878 }
879
880 } /* end ALIEN_LOCK_ON */
881
882
883
884 /*
885 ** SIMPLE_COLLISION
886 **
887 ** Collision between two objects only
888 */
simple_collision(obj1,obj2,bnce,kill)889 int simple_collision(obj1, obj2, bnce, kill)
890 Sprite *obj1, *obj2;
891 int bnce, kill;
892 {
893
894 float dx, dy, dist;
895 int k = 0;
896
897 if (obj1->state || obj2->state) return(0);
898
899 dx = obj1->x - obj2->x;
900 dy = obj1->y - obj2->y;
901 dist = sqrt(dx * dx + dy * dy);
902
903 if (dist < obj1->ho2 + obj2->ho2) {
904
905 if (bnce) {
906 if (speed(obj1) > speed(obj2)) {
907 k = bounce_objects(obj1, obj2);
908 } else {
909 k = bounce_objects(obj2, obj1);
910 }
911 }
912
913 if (k && kill) {
914 obj1->state = DYING;
915 obj1->rotation = 1;
916 obj2->state = DYING;
917 obj2->rotation = 1;
918 }
919
920 return(1);
921
922 }
923
924 return(0);
925
926 } /* end SIMPLE_COLLISION */
927
928
929
930
931 /*
932 ** CHECK_COLLISION
933 **
934 ** If there is a collision, this routine returns a pointer to the
935 ** member of obj2 which was hit. If bounce is set, then obj1 will
936 ** appear to "bounce" off of obj2.
937 **
938 ** This routine deals with an object AND all its children.
939 */
check_collision(obj1,ob2,bnce,kill)940 Sprite *check_collision(obj1, ob2, bnce, kill)
941 Sprite *obj1, *ob2;
942 int bnce, kill;
943 {
944
945 float dx, dy, dist;
946 int hit;
947 int ds = 0;
948 Sprite *obj2;
949 Sprite *collide = NULL;
950
951 /* Basic collision check - radius test, assumes objects have */
952 /* aspect ratio of ONE. */
953
954 do {
955
956 ds = 0;
957
958 if (obj1->state == EXPLODING && obj1->death_sprite) {
959 check_collision(obj1->death_sprite, ob2, bnce, 0);
960 ds = 1;
961 }
962
963 obj2 = ob2;
964
965 do {
966
967 if (obj2->state == EXPLODING && obj2->death_sprite) {
968 check_collision(obj1, obj2->death_sprite, bnce, kill);
969 }
970
971 if (obj1->state <= DYING && obj2->state <= DYING && !obj1->bounced) {
972
973 dx = obj1->x - obj2->x;
974 dy = obj1->y - obj2->y;
975 dist = sqrt(dx * dx + dy * dy);
976
977 if (dist < obj1->ho2 + obj2->ho2) {
978 if (bnce) hit = bounce_objects(obj1, obj2);
979 if (hit && !ds) {
980 if (kill) {
981 if (coop) X_draw_link(P, BLACK);
982 obj1->state = DYING;
983 obj1->rotation = 1;
984 (*obj2->kill_func)(obj2);
985 }
986 collide = obj2;
987 }
988 }
989 }
990
991 } while (obj2 = obj2->next_draw);
992
993 } while (obj1 = obj1->next_draw);
994
995 return(collide);
996
997 } /* end CHECK_COLLISION */
998
999
1000
1001 /*
1002 ** BOUNCE_OBJECTS
1003 **
1004 ** Bounces obj1 off of obj2, assuming obj2 has a circular shape (pretty
1005 ** good for most oids!)
1006 **
1007 ** I thought this routine would be pretty easy, but the physics are
1008 ** not as trivial as I thought. There's certainly room for improvement
1009 ** as well.
1010 */
bounce_objects(obj1,obj2)1011 int bounce_objects(obj1, obj2)
1012 Sprite *obj1, *obj2;
1013 {
1014
1015 float ang1, ang2, alpha, old_xv, old_yv;
1016 Sprite *fast, *slow;
1017 float x2t, y2t, rel_v, red_m;
1018 int flag = 0;
1019
1020
1021 red_m = (obj1->mass + obj2->mass);
1022
1023 if (speed(obj1) > speed(obj2)) {
1024 fast = obj1;
1025 slow = obj2;
1026 } else {
1027 fast = obj2;
1028 slow = obj1;
1029 }
1030
1031 old_xv = fast->x_vec;
1032 old_yv = fast->y_vec;
1033
1034 fast->x_vec += fabs(slow->x_vec) * slow->mass / red_m;
1035 fast->y_vec += fabs(slow->y_vec) * slow->mass / red_m;
1036
1037 slow->x_vec += old_xv * fast->mass / red_m;
1038 slow->y_vec += old_yv * fast->mass / red_m;
1039
1040 x2t = fast->x - slow->x;
1041 y2t = fast->y - slow->y;
1042
1043 ang1 = atan2(-y2t,x2t); /* Should make an ATAN table!! */
1044 ang2 = atan2(-fast->y_vec,fast->x_vec);
1045
1046 alpha = PI + ang2;
1047 alpha = 2 * ang1 - alpha;
1048
1049 ang2 += PI;
1050
1051 if (ang1 < 0.0) ang1 += 2*PI;
1052
1053 /* If just "bumped", then we'll let you live - this time! */
1054
1055 rel_v = sqrt((obj1->x_vec - obj2->x_vec)*(obj1->x_vec - obj2->x_vec) + (obj1->y_vec - obj2->y_vec)*(obj1->y_vec - obj2->y_vec));
1056
1057
1058 if (rel_v < DEATH_THRESH) {
1059 fast->x_vec = -old_xv; /* Pushed away */
1060 fast->y_vec = -old_yv;
1061 if (coop)
1062 if (!(P->state || P->next->state)) {
1063 link.ang_vec = -link.ang_vec;
1064 }
1065 } else {
1066 fast->x_vec = fabs(fast->x_vec) * cos(alpha);
1067 fast->y_vec = -fabs(fast->y_vec) * sin(alpha);
1068 flag = 1;
1069 }
1070
1071 obj1->bounced = 5;
1072
1073 return(flag);
1074
1075 } /* end BOUNCE_OBJECTS */
1076
1077
1078
1079
1080 /*
1081 ** EXPLODE_OBJECT
1082 **
1083 ** Blow something to bits, and make sure the bits behave as they should.
1084 */
explode_object(obj)1085 void explode_object(obj)
1086 Sprite *obj;
1087 {
1088
1089 int ang;
1090 Sprite *carnage;
1091
1092 X_clear_one_object(obj);
1093
1094 X_flash_screen(2);
1095
1096 obj->state = EXPLODING;
1097 obj->thrust = 0;
1098 if (--obj->lives < 0) obj->lives = 0;
1099 if (num_players > 1 && obj->score > 500) obj->score -= 500;
1100 update_score();
1101
1102 make_burst(obj, 1);
1103
1104 if (carnage = obj->death_sprite) {
1105
1106 do {
1107
1108 ang = (obj->curr_map + (int) (random_num(0.0, (float) obj->num_pixmaps))) % obj->num_pixmaps;
1109
1110 carnage->state = DYING;
1111
1112 carnage->x = obj->x;
1113 carnage->y = obj->y;
1114
1115 carnage->x_vec = -CARNAGE_SPEED*sin_table[ang] + obj->x_vec;
1116 carnage->y_vec = -CARNAGE_SPEED*cos_table[ang] + obj->y_vec;
1117
1118 } while (carnage = carnage->next);
1119
1120 }
1121
1122 if (!coop) {
1123 obj->x = random_num(0.0, (float) (vdevice.sizeSx));
1124 obj->y = random_num((float) P->height, (float) (vdevice.sizeSy));
1125 obj->ox = obj->x;
1126 obj->oy = obj->y;
1127 }
1128
1129 } /* end EXPLODE_OBJECT */
1130
1131
1132
1133 /*
1134 ** WARP_OBJECT
1135 **
1136 ** Send someone into hyperspace.
1137 */
warp_object(obj)1138 void warp_object(obj)
1139 Sprite *obj;
1140 {
1141
1142 do {
1143 obj->x = random_num(0.0, (float) vdevice.sizeSx);
1144 obj->y = random_num(0.0, (float) vdevice.sizeSy);
1145 } while (check_collision(obj, Big_O, 0, 0));
1146
1147 obj->state = NORMAL;
1148 obj->state_ctr = 0;
1149 obj->x_vec = 0.0;
1150 obj->y_vec = 0.0;
1151 obj->rotation = 0;
1152
1153 } /* end WARP_OBJECT */
1154
1155
1156
1157 /*
1158 ** REVIVE_PLAYER
1159 **
1160 ** You're lucky it's just a game, or this routine wouldn't exist.
1161 ** (That is, if you don't believe in reincarnation)
1162 */
revive_player(player)1163 void revive_player(player)
1164 Sprite *player;
1165 {
1166
1167 Sprite *carnage;
1168
1169 if (coop) X_draw_link(P, BLACK);
1170
1171 player->state = NORMAL;
1172 player->state_ctr = 0;
1173 player->point_angle = 0.0;
1174 player->x_vec = 0.0;
1175 player->y_vec = 0.0;
1176 player->rotation = 0;
1177 player->thrust = 0;
1178 player->engine = 0;
1179 player->pixmaps = player->orig_pixmaps;
1180 player->clipmasks = player->orig_clipmasks;
1181
1182 put_inside_window(player);
1183
1184 if (carnage = player->death_sprite) {
1185
1186 do {
1187 carnage->state = NORMAL;
1188
1189 carnage->x = random_num(0.0, (float) (vdevice.sizeSx));
1190 carnage->y = random_num(0.0, (float) (vdevice.sizeSy));
1191
1192 carnage->x_vec = (player->x - carnage->x) / 30;
1193 carnage->y_vec = (player->y - carnage->y) / 30;
1194
1195 } while (carnage = carnage->next);
1196
1197 }
1198
1199
1200 /* Here's where things are a bit tricky. If one of the linked */
1201 /* players has died, the other one remains free to fly around */
1202 /* unencumbered by the other */
1203
1204 if (coop && (player == P || player == P->next)) {
1205
1206 if (player == P) {
1207 player->x = P->next->x + 2*link.length*sin(link.ang);
1208 player->y = P->next->y + 2*link.length*cos(link.ang);
1209 link.x_vec = P->next->x_vec;
1210 link.y_vec = P->next->y_vec;
1211 } else {
1212 player->x = P->x - 2*link.length*sin(link.ang);
1213 player->y = P->y - 2*link.length*cos(link.ang);
1214 link.x_vec = P->x_vec;
1215 link.y_vec = P->y_vec;
1216 }
1217
1218 player->ox = player->x;
1219 player->oy = player->y;
1220
1221 link.x = fabs(P->x + P->next->x) / 2;
1222 link.y = fabs(P->y + P->next->y) / 2;
1223
1224 } else {
1225
1226 player->state = REVIVING;
1227
1228 }
1229
1230 } /* end REVIVE_PLAYER */
1231
1232
1233
1234 /*
1235 ** UPDATE_BURSTS
1236 **
1237 ** Keep track of all the various supernova explosions
1238 */
update_bursts()1239 void update_bursts()
1240 {
1241
1242 int i, j, flag = 0;
1243 struct explosion *b, *b1;
1244
1245 for (i=0;i<num_bursts;i++) {
1246
1247 burst[i].radius += 4*burst[i].dir;
1248 burst[i].x += burst[i].x_vec;
1249 burst[i].y += burst[i].y_vec;
1250
1251 if (burst[i].radius > burst[i].max_radius || burst[i].radius < 1) {
1252 if (!flag) {
1253 X_draw_bursts(0);
1254 flag = 1;
1255 }
1256
1257 for (j=i;j<num_bursts-1;j++) {
1258 b = &burst[i];
1259 b1 = &burst[i+1];
1260
1261 b->x = b1->x;
1262 b->y = b1->y;
1263 b->radius = b1->radius;
1264 b->color = b1->color;
1265 }
1266
1267 num_bursts--;
1268 }
1269 }
1270
1271 } /* end UPDATE_BURSTS */
1272
1273
1274
1275
1276 /*
1277 ** MAKE_BURST
1278 **
1279 ** Create a new supernova explosion
1280 */
make_burst(obj,dir)1281 void make_burst(obj, dir)
1282 Sprite *obj;
1283 int dir;
1284 {
1285
1286 burst[num_bursts].x = obj->x;
1287 burst[num_bursts].y = obj->y;
1288 burst[num_bursts].color = obj->burst_color;
1289 burst[num_bursts].max_radius = obj->height;
1290
1291 if (dir < 0) {
1292 burst[num_bursts].radius = obj->height;
1293 } else {
1294 burst[num_bursts].radius = 1;
1295 }
1296
1297 burst[num_bursts].x_vec = (int) obj->x_vec;
1298 burst[num_bursts].y_vec = (int) obj->y_vec;
1299 burst[num_bursts++].dir = dir;
1300
1301 } /* end MAKE_BURST */
1302
1303
1304
1305
1306 /*
1307 ** UPDATE_SHOTS
1308 **
1309 ** Keep track of everybody's fire
1310 */
update_shots(obj)1311 Sprite *update_shots(obj)
1312 Sprite *obj;
1313 {
1314
1315 int i;
1316 struct Shot *curr;
1317 Sprite *check;
1318 Sprite *hit_object = NULL;
1319
1320 do {
1321
1322 if (obj->shots) {
1323
1324 for (i=0;i<obj->shots;i++) {
1325
1326 curr = obj->S[i];
1327
1328 curr->ox = curr->x;
1329 curr->oy = curr->y;
1330
1331 curr->x += curr->x_vec;
1332 curr->y += curr->y_vec;
1333
1334 if (--curr->clock == 1)
1335 erase_shot(obj, i);
1336
1337 if (curr->x > vdevice.sizeSx) curr->x = 0;
1338 if (curr->x < 0) curr->x = vdevice.sizeSx;
1339
1340 if (curr->y > vdevice.sizeSy) curr->y = P->height + 3;
1341 if (curr->y < P->height + 3) curr->y = vdevice.sizeSy;
1342
1343 /* Collision detection of shots */
1344
1345 check = shot_collide(curr, Big_O);
1346 if (check) hit_object = check;
1347 if (!hit_object) {
1348 check = shot_collide(curr, P);
1349 if (check) hit_object = check;
1350 }
1351 if (!Slrb->state && !hit_object) {
1352 check = shot_collide(curr, Slrb);
1353 if (check) hit_object = check;
1354 }
1355 if (!Homer->state && !hit_object) {
1356 check = shot_collide(curr, Homer);
1357 if (check) hit_object = check;
1358 }
1359 if (!Resur->state && !hit_object) {
1360 check = shot_collide(curr, Resur);
1361 if (check) hit_object = check;
1362 }
1363
1364 if (hit_object == obj) hit_object = NULL;
1365
1366 if (hit_object) {
1367 if (obj->wpn != LASER) erase_shot(obj, i);
1368 obj->score += hit_object->value;
1369 obj->hits++;
1370 update_score();
1371 return(hit_object);
1372 }
1373
1374 }
1375 }
1376
1377 } while (obj = obj->next);
1378
1379 return(hit_object);
1380
1381 } /* end UPDATE_SHOTS */
1382
1383
1384
1385 /*
1386 ** SHOT_COLLIDE
1387 **
1388 ** Test whether the input shot has hit a particular object.
1389 **
1390 */
shot_collide(this_shot,obj)1391 Sprite *shot_collide(this_shot, obj)
1392 struct Shot *this_shot;
1393 Sprite *obj;
1394 {
1395
1396 float dx, dy, dist;
1397 Sprite *hit;
1398 int i;
1399
1400 do {
1401
1402 if (obj->state == EXPLODING && obj->death_sprite) {
1403 hit = shot_collide(this_shot, obj->death_sprite);
1404 if (hit) return(hit);
1405 }
1406
1407 if (obj->state == NORMAL) {
1408
1409 for (i=0;i<this_shot->num_kill_pts;i++) {
1410 dx = this_shot->x + this_shot->pts_x[this_shot->kill_pts[i]] - obj->x;
1411 dy = this_shot->y + this_shot->pts_y[this_shot->kill_pts[i]] - obj->y;
1412 dist = sqrt(dx * dx + dy * dy);
1413
1414 if (dist < obj->ho2) {
1415 return(obj);
1416 }
1417 }
1418 }
1419
1420 } while (obj = obj->next_draw);
1421
1422 return(NULL);
1423
1424 } /* end SHOT_COLLIDE */
1425
1426
1427
1428
1429 /*
1430 ** ERASE_SHOT
1431 **
1432 ** Shots are represented by a linked list, so when we erase a
1433 ** shot, we'll take all the shots to the right of it and shift them
1434 ** one to the left, overwriting the shot to be erased.
1435 */
erase_shot(obj,n)1436 void erase_shot(obj, n)
1437 Sprite *obj;
1438 int n;
1439 {
1440
1441 int i,j;
1442 struct Shot *curr, *curr1;
1443
1444 X_clear_shots(obj);
1445 X_copy_object_to_window(obj);
1446
1447 for (i=n;i<obj->shots-1;i++) {
1448 curr = obj->S[i];
1449 curr1 = obj->S[i+1];
1450
1451 curr->clock = curr1->clock;
1452 curr->x = curr1->x;
1453 curr->y = curr1->y;
1454 curr->ox = curr1->ox;
1455 curr->oy = curr1->oy;
1456 curr->x_vec = curr1->x_vec;
1457 curr->y_vec = curr1->y_vec;
1458
1459 for (j=0;j<5;j++) {
1460 curr->pts_x[j] = curr1->pts_x[j];
1461 curr->pts_y[j] = curr1->pts_y[j];
1462 }
1463
1464 }
1465
1466
1467 obj->shots--;
1468
1469 } /* end ERASE_SHOT */
1470
1471
1472
1473 /*
1474 ** CREATE_NEW_SHOT
1475 **
1476 ** Speaks for itself, don't it?
1477 */
create_new_shot(obj)1478 void create_new_shot(obj)
1479 Sprite *obj;
1480 {
1481
1482 int i, j;
1483
1484 i=obj->shots++;
1485 obj->shots_fired++;
1486
1487 obj->S[i]->clock = Weapon[obj->wpn].shot_life;
1488
1489 obj->S[i]->num_kill_pts = Weapon[obj->wpn].num_kill_pts;
1490
1491 for (j=0;j<obj->S[i]->num_kill_pts;j++)
1492 obj->S[i]->kill_pts[j] = Weapon[obj->wpn].kill_pts[j];
1493
1494 /* Define a graphical representation of the shot, based on the */
1495 /* current weapon */
1496
1497 for (j=0;j<5;j++) {
1498 obj->S[i]->pts_x[j] = Weapon[obj->wpn].pts_x[j] * cos_table[obj->curr_map] - Weapon[obj->wpn].pts_y[j] * sin_table[obj->curr_map];
1499 obj->S[i]->pts_y[j] = -Weapon[obj->wpn].pts_x[j] * sin_table[obj->curr_map] - Weapon[obj->wpn].pts_y[j] * cos_table[obj->curr_map];
1500 }
1501
1502 /* Place the shot according to how the shooter is oriented */
1503
1504 obj->S[i]->x = obj->S[i]->ox = hot_spot_x*cos_table[obj->curr_map] - (hot_spot_y + Weapon[obj->wpn].size / 2)*sin_table[obj->curr_map] + obj->x;
1505 obj->S[i]->y = obj->S[i]->oy = -hot_spot_x*sin_table[obj->curr_map] - (hot_spot_y + Weapon[obj->wpn].size / 2)*cos_table[obj->curr_map] + obj->y;
1506
1507 /* Give it some kick */
1508
1509 obj->S[i]->x_vec = - (float) Weapon[obj->wpn].shot_speed*sin_table[obj->curr_map];
1510 obj->S[i]->y_vec = - (float) Weapon[obj->wpn].shot_speed*cos_table[obj->curr_map];
1511
1512 /* The stock weapon has inertia */
1513
1514 if (obj->wpn != LASER) {
1515 obj->S[i]->x_vec += obj->x_vec;
1516 obj->S[i]->y_vec += obj->y_vec;
1517 }
1518
1519 /* A little momentum conservation */
1520
1521 if (!coop) {
1522 obj->x_vec -= 0.01 * obj->S[i]->x_vec;
1523 obj->y_vec -= 0.01 * obj->S[i]->y_vec;
1524 }
1525
1526 } /* end CREATE_NEW_SHOT */
1527
1528
1529
1530 /*
1531 ** UPDATE_SCORE
1532 **
1533 */
update_score()1534 void update_score()
1535 {
1536
1537 X_draw_status_bar();
1538
1539 } /* end UPDATE_SCORE */
1540
1541
1542 /*
1543 ** KILL_SHARD
1544 **
1545 ** The remnants of a blown-up object finally
1546 ** bite the dust.
1547 */
kill_shard(the_shard)1548 void kill_shard(the_shard)
1549 Sprite *the_shard;
1550 {
1551 int fl = 0;
1552
1553 X_clear_one_object(the_shard);
1554 X_copy_object_to_window(P);
1555 X_copy_object_to_window(Slrb);
1556 X_copy_object_to_window(Resur);
1557 the_shard->state = DEAD;
1558 the_shard->state_ctr = 0;
1559 make_burst(the_shard, 1);
1560
1561 /* If you're lucky, a power up will appear */
1562
1563 if (random_num(0.0, 10.0) > 9.0 && Power_Up->state) {
1564 Power_Up->state = NORMAL;
1565 Power_Up->x = the_shard->x;
1566 Power_Up->y = the_shard->y;
1567 Power_Up->x_vec = the_shard->x_vec;
1568 Power_Up->y_vec = the_shard->y_vec;
1569 fl = 1;
1570 }
1571
1572 if (random_num(0.0, 10.0) < 0.3 && num_players > 1 && Meta->state && !fl && level > 1) {
1573 Meta->state = NORMAL;
1574 Meta->x = the_shard->x;
1575 Meta->y = the_shard->y;
1576 Meta->x_vec = the_shard->x_vec;
1577 Meta->y_vec = the_shard->y_vec;
1578 }
1579
1580 } /* end KILL_SHARD */
1581
1582
1583
1584 /*
1585 ** DESTROY_OBJECT
1586 **
1587 ** Generic object destruction.
1588 */
destroy_object(obj)1589 void destroy_object(obj)
1590 Sprite *obj;
1591 {
1592
1593 X_clear_one_object(obj);
1594 X_copy_object_to_window(obj);
1595 obj->state = DEAD;
1596 make_burst(obj, 1);
1597
1598 } /* end DESTROY_OBJECT */
1599
1600
1601
1602 /*
1603 ** DESTROY_HOMER
1604 **
1605 ** Doh!
1606 */
destroy_homer(obj)1607 void destroy_homer(obj)
1608 Sprite *obj;
1609 {
1610
1611 X_clear_one_object(obj);
1612 X_copy_object_to_window(obj);
1613 draw_homer_trail(2);
1614 obj->state = DEAD;
1615 make_burst(obj, 1);
1616
1617 } /* end DESTROY_OBJECT */
1618
1619
1620
1621 /*
1622 ** KILL_POWER_UP
1623 **
1624 ** Someone has run into a power up, and the lucky ship will
1625 ** now reap the benefits.
1626 */
kill_power_up(the_pu,the_lucky)1627 void kill_power_up(the_pu, the_lucky)
1628 Sprite *the_pu, *the_lucky;
1629 {
1630
1631 Pixmap *old_pixmap;
1632
1633 X_clear_one_object(the_pu);
1634 X_copy_object_to_window(the_pu);
1635 the_pu->state = DEAD;
1636 make_burst(the_pu, 1);
1637
1638 if (the_pu == Power_Up) the_lucky->wpn = LASER;
1639
1640 /* Metamorphosize! (sp?) */
1641
1642 if (the_pu == Meta) {
1643
1644 old_pixmap = the_lucky->pixmaps;
1645
1646 do {
1647 switch ((int) random_num(0.0, 5.0)) {
1648
1649 case 1:
1650 the_lucky->pixmaps = P->pixmaps;
1651 the_lucky->clipmasks = P->clipmasks;
1652 break;
1653 case 2:
1654 the_lucky->pixmaps = P->next->pixmaps;
1655 the_lucky->clipmasks = P->next->clipmasks;
1656 break;
1657 case 3:
1658 the_lucky->pixmaps = Slrb->pixmaps;
1659 the_lucky->clipmasks = Slrb->clipmasks;
1660 break;
1661 case 4:
1662 the_lucky->pixmaps = Resur->pixmaps;
1663 the_lucky->clipmasks = Resur->clipmasks;
1664 break;
1665 case 5:
1666 default:
1667 the_lucky->pixmaps = Med_O[0]->pixmaps;
1668 the_lucky->clipmasks = Med_O[0]->clipmasks;
1669 break;
1670
1671 }
1672
1673 } while (the_lucky->pixmaps == old_pixmap);
1674 }
1675
1676 } /* end KILL_POWER_UP */
1677
1678
1679 /*
1680 ** DESTROY_OID
1681 **
1682 ** If you're a good player, this gets called quite a bit.
1683 */
destroy_oid(the_oid)1684 void destroy_oid(the_oid)
1685 Sprite *the_oid;
1686 {
1687
1688 Sprite *debris, *player;
1689 int i, max_oids;
1690
1691 max_oids = INIT_BIG_OIDS + floor(((float)level - 1.0) / 2.0);
1692
1693 X_clear_one_object(the_oid);
1694 X_copy_object_to_window(Big_O);
1695 the_oid->state = EXPLODING;
1696 make_burst(the_oid, 1);
1697
1698 /* Handle the destruction of the last oid */
1699
1700 if (++oids_shot >= max_oids * (1 + OID_DIVIDE + OID_DIVIDE * OID_DIVIDE)) {
1701 oids_shot = 0;
1702 warp_levels = MAPS_PER_360; /* Don't ask */
1703 level++;
1704 max_oids = INIT_BIG_OIDS + floor(((float)level - 1.0) / 2.0);
1705 if (max_oids > MAX_BIG_OIDS) max_oids = MAX_BIG_OIDS;
1706 for (i=0;i<max_oids;i++) revive_oid(&Big_O[i], 1);
1707 player = P;
1708 do {
1709 revive_player(player);
1710 X_clear_shots(player);
1711 X_copy_object_to_window(player);
1712 player->shots = 0;
1713 } while (player = player->next);
1714 Slrb->state = Power_Up->state = Homer->state = Resur->state = DEAD;
1715 Slrb->shots = 0;
1716 X_flash_screen(3);
1717 return;
1718 }
1719
1720 /* Now that you've destroyed the oid, deal with its kids */
1721
1722 if (debris = the_oid->death_sprite) {
1723
1724 do {
1725
1726 debris->x = the_oid->x;
1727 debris->y = the_oid->y;
1728
1729 } while (debris = debris->next);
1730
1731 }
1732
1733
1734 } /* end DESTROY_OID */
1735
1736
1737 /*
1738 ** REVIVE_OID
1739 **
1740 ** Back from the dead, more oids!
1741 */
revive_oid(obj,flag)1742 void revive_oid(obj, flag)
1743 Sprite *obj;
1744 int flag;
1745 {
1746
1747 if (flag) {
1748 obj->state = NORMAL;
1749
1750 obj->x = random_num(0.0, (float) vdevice.sizeSx);
1751 obj->y = random_num((float) P->height, (float) vdevice.sizeSy);
1752
1753 if (obj->death_sprite) revive_oid(obj->death_sprite, 0);
1754
1755 } else {
1756
1757 do {
1758
1759 if (obj->death_sprite) revive_oid(obj->death_sprite, 0);
1760
1761 obj->state = NORMAL;
1762
1763 obj->x = random_num(0.0, (float) vdevice.sizeSx);
1764 obj->y = random_num((float) P->height, (float) vdevice.sizeSy);
1765
1766 } while (obj = obj->next);
1767
1768 }
1769
1770 } /* end REVIVE_OID */
1771
1772
1773
1774 /*
1775 ** PAUSE_GAME
1776 **
1777 */
pause_game()1778 void pause_game()
1779 {
1780
1781 int c;
1782
1783 do {
1784
1785 X_frontbuf();
1786
1787 X_color(YELLOW);
1788
1789 X_string("G A M E P A U S E D", (int) vdevice.sizeSy / 2 - 30);
1790
1791 X_color(CYAN);
1792
1793 X_string("Hit any key to resume", (int) vdevice.sizeSy / 2);
1794
1795 c = X_checkkey();
1796 X_update(1);
1797
1798 } while (!c);
1799
1800 X_clear();
1801 X_backbuf();
1802
1803 } /* end PAUSE_GAME */
1804
1805
1806
1807 /*
1808 ** DRAW_LEVEL_WARP
1809 **
1810 */
draw_level_warp()1811 void draw_level_warp()
1812 {
1813
1814 char str1[50];
1815
1816 X_frontbuf();
1817
1818 X_color(YELLOW);
1819
1820 sprintf(str1, "Warping to Level %d", level);
1821 X_string(str1, (int) vdevice.sizeSy / 2 - 70);
1822
1823 X_backbuf();
1824
1825 } /* end DRAW_LEVEL_WARP */
1826
1827
1828
1829 /*
1830 ** DRAW_GAME_OVER
1831 **
1832 */
draw_game_over()1833 void draw_game_over()
1834 {
1835
1836 char str1[50];
1837 Sprite *winner;
1838 int acc;
1839
1840 X_frontbuf();
1841
1842 X_color(RED);
1843
1844 X_string("G A M E O V E R", (int) vdevice.sizeSy / 2 - 70);
1845
1846 if (num_players == 1) {
1847
1848 X_color(YELLOW);
1849 sprintf(str1, "Final Score: %d", P->score);
1850 X_string(str1, (int) vdevice.sizeSy / 2);
1851
1852 X_color(CYAN);
1853 sprintf(str1, "Shots fired: %d", P->shots_fired);
1854 X_string(str1, (int) vdevice.sizeSy / 2 + 50);
1855 sprintf(str1, "Number of hits: %d", P->hits);
1856 X_string(str1, (int) vdevice.sizeSy / 2 + 70);
1857
1858 if (P->shots_fired == 0) {
1859 acc = 0;
1860 } else {
1861 acc = (int) ((float) P->hits / (float) P->shots_fired * 100.0);
1862 }
1863
1864 sprintf(str1, "Accuracy: %d%%", acc);
1865 X_string(str1, (int) vdevice.sizeSy / 2 + 90);
1866
1867 } else {
1868 if (P->score > P->next->score) {
1869 winner = P;
1870 X_color(CYAN);
1871 } else {
1872 winner = P->next;
1873 X_color(YELLOW);
1874 }
1875
1876 sprintf(str1, "%s wins!", winner->name);
1877 X_string(str1, (int) vdevice.sizeSy / 2 - 40);
1878
1879 X_color(CYAN);
1880 sprintf(str1, "%s:", P->name);
1881 X_string(str1, (int) vdevice.sizeSy / 2 + 10);
1882 sprintf(str1, "Shots fired: %d", P->shots_fired);
1883 X_string(str1, (int) vdevice.sizeSy / 2 + 30);
1884 sprintf(str1, "Number of hits: %d", P->hits);
1885 X_string(str1, (int) vdevice.sizeSy / 2 + 50);
1886
1887 if (P->shots_fired == 0) {
1888 acc = 0;
1889 } else {
1890 acc = (int) ((float) P->hits / (float) P->shots_fired * 100.0);
1891 }
1892
1893 sprintf(str1, "Accuracy: %d%%", acc);
1894 X_string(str1, (int) vdevice.sizeSy / 2 + 70);
1895
1896 X_color(YELLOW);
1897 sprintf(str1, "%s:", P->next->name);
1898 X_string(str1, (int) vdevice.sizeSy / 2 + 120);
1899 sprintf(str1, "Shots fired: %d", P->next->shots_fired);
1900 X_string(str1, (int) vdevice.sizeSy / 2 + 140);
1901 sprintf(str1, "Number of hits: %d", P->next->hits);
1902 X_string(str1, (int) vdevice.sizeSy / 2 + 160);
1903
1904 if (P->next->shots_fired == 0) {
1905 acc = 0;
1906 } else {
1907 acc = (int) ((float) P->next->hits / (float) P->next->shots_fired * 100.0);
1908 }
1909
1910 sprintf(str1, "Accuracy: %d%%", acc);
1911 X_string(str1, (int) vdevice.sizeSy / 2 + 180);
1912 }
1913
1914
1915 X_backbuf();
1916
1917 X_color(BLACK);
1918
1919 } /* end DRAW_GAME_OVER */
1920
1921
1922
1923 /*
1924 ** SPEED
1925 **
1926 */
speed(obj)1927 float speed(obj)
1928 Sprite *obj;
1929 {
1930
1931 return(sqrt(obj->x_vec * obj->x_vec + obj->y_vec * obj->y_vec));
1932
1933 } /* end SPEED */
1934
1935
1936 /*
1937 ** DISTANCE
1938 **
1939 */
distance(obj1,obj2)1940 float distance(obj1, obj2)
1941 Sprite *obj1, *obj2;
1942 {
1943
1944 return(fabs(obj1->x - obj2->x) + fabs(obj1->y - obj2->y));
1945
1946 } /* end DISTANCE */
1947
1948
1949
1950
1951 /*
1952 ** RANDOM_NUM
1953 **
1954 */
random_num(low,high)1955 float random_num(low, high)
1956 float low, high;
1957 {
1958
1959 float out;
1960 int num;
1961
1962 srand(r_num); /* use prev # as seed for random number generator*/
1963
1964 num = rand();
1965 r_num = num;
1966
1967 out = ((float) num) / 2147483647.0 * (high - low) + low;
1968
1969 return(out);
1970
1971 } /* end RANDOM_NUM */
1972
1973
1974 /*
1975 ** DO_NOTHING_FOR_A_WHILE
1976 **
1977 */
do_nothing_for_a_while()1978 void do_nothing_for_a_while()
1979 {
1980
1981 double x;
1982 unsigned int d;
1983
1984 d = delay * 1000;
1985
1986 x = 0.37;
1987
1988 do {
1989
1990 x = cos(x);
1991
1992 } while (d--);
1993
1994 } /* end DO_NOTHING_FOR_A_WHILE */
1995
1996 /*
1997 ** PUT_INSIDE_WINDOW
1998 **
1999 */
put_inside_window(obj)2000 void put_inside_window(obj)
2001 Sprite *obj;
2002 {
2003
2004 if (obj->x > vdevice.sizeSx) obj->x = 0;
2005 if (obj->x < 0) obj->x = vdevice.sizeSx;
2006
2007 if (obj->y > vdevice.sizeSy) obj->y = P->height + 3;
2008 if (obj->y < P->height + 3) obj->y = vdevice.sizeSy;
2009
2010 } /* end PUT_INSIDE_WINDOW */
2011