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