1 // SVG Spacewar is copyright 2005 by Nigel Tao: nigel.tao@myrealbox.com
2 // Licenced under the GNU GPL.
3 // Developed on cairo version 0.4.0.
5 // 2005-03-31: Version 0.1.
11 #include <gdk/gdkkeysyms.h>
13 #include <sys/timeb.h>
18 #define TWO_PI (2*M_PI)
21 // trig computations (and x, y, velocity, etc). are made in fixed point arithmetic
22 #define FIXED_POINT_SCALE_FACTOR 1024
23 #define FIXED_POINT_HALF_SCALE_FACTOR 32
25 // discretization of 360 degrees
26 #define NUMBER_OF_ROTATION_ANGLES 60
27 #define RADIANS_PER_ROTATION_ANGLE (TWO_PI / NUMBER_OF_ROTATION_ANGLES)
29 // equivalent to 25 fps
30 #define MILLIS_PER_FRAME 40
32 // a shot every 9/25 seconds = 8 ticks between shots
33 #define TICKS_BETWEEN_FIRE 8
35 // fudge this for bigger or smaller ships
36 #define GLOBAL_SHIP_SCALE_FACTOR 0.8
38 #define SHIP_ACCELERATION_FACTOR 1
39 #define SHIP_MAX_VELOCITY (10 * FIXED_POINT_SCALE_FACTOR)
40 #define SHIP_RADIUS ((int) (38 * FIXED_POINT_SCALE_FACTOR * GLOBAL_SHIP_SCALE_FACTOR))
42 #define SHIP_MAX_ENERGY 1000
43 #define DAMAGE_PER_MISSILE 200
44 #define ENERGY_PER_MISSILE 10
46 // bounce damage depends on how fast you're going
47 #define DAMAGE_PER_SHIP_BOUNCE_DIVISOR 3
49 #define NUMBER_OF_STARS 20
51 #define MAX_NUMBER_OF_MISSILES 60
53 #define MISSILE_RADIUS (4 * FIXED_POINT_SCALE_FACTOR)
54 #define MISSILE_SPEED 8
55 #define MISSILE_TICKS_TO_LIVE 60
56 #define MISSILE_EXPLOSION_TICKS_TO_LIVE 6
58 //------------------------------------------------------------------------------
71 // 0 is straight up, (NUMBER_OF_ROTATION_ANGLES / 4) is pointing right
74 // used for collision detection - we presume that an object is equivalent
75 // to its bounding circle, rather than trying to do something fancy.
84 gboolean is_thrusting;
85 gboolean is_turning_left;
86 gboolean is_turning_right;
90 RGB_t secondary_color;
92 int ticks_until_can_fire;
107 RGB_t secondary_color;
110 gboolean has_exploded;
122 //------------------------------------------------------------------------------
123 // Forward definitions of functions
125 static void apply_physics (physics_t *);
126 static void apply_physics_to_player (player_t *);
127 static gboolean check_for_collision (physics_t *, physics_t *);
128 static void draw_energy_bar (cairo_t *, player_t *);
129 static void draw_flare (cairo_t *, RGB_t);
130 static void draw_missile (cairo_t *, missile_t *);
131 static void draw_exploded_missile (cairo_t *, missile_t *);
132 static void draw_ship_body (cairo_t *, player_t *);
133 static void draw_star (cairo_t * cr);
134 static void draw_turning_flare (cairo_t *, RGB_t, int);
135 static void enforce_minimum_distance (physics_t *, physics_t *);
136 static long get_time_millis (void);
137 static void init_missiles_array (void);
138 static void init_stars_array (void);
139 static void init_trigonometric_tables (void);
140 static void on_collision (player_t *, missile_t *);
141 static gint on_expose_event (GtkWidget *, GdkEventExpose *);
142 static gint on_key_event (GtkWidget *, GdkEventKey *, gboolean);
143 static gint on_key_press (GtkWidget *, GdkEventKey *);
144 static gint on_key_release (GtkWidget *, GdkEventKey *);
145 static gint on_timeout (gpointer);
146 static void reset ();
147 static void scale_for_aspect_ratio (cairo_t *, int, int);
148 static void show_text_message (cairo_t *, int, int, const char *);
150 //------------------------------------------------------------------------------
152 static player_t player1;
153 static player_t player2;
155 //------------------------------------------------------------------------------
157 static missile_t missiles[MAX_NUMBER_OF_MISSILES];
158 static int next_missile_index = 0;
161 init_missiles_array ()
165 for (i = 0; i < MAX_NUMBER_OF_MISSILES; i++)
167 missiles[i].p.radius = MISSILE_RADIUS;
168 missiles[i].is_alive = FALSE;
172 //------------------------------------------------------------------------------
174 static star_t stars[NUMBER_OF_STARS];
180 return (double) rand () / RAND_MAX;
185 return rand () * 32768 + rand ();
194 for (i = 0; i < NUMBER_OF_STARS; i++)
196 stars[i].x = random () % WIDTH;
197 stars[i].y = random () % HEIGHT;
198 stars[i].rotation = drand48 () * TWO_PI;
199 stars[i].scale = 0.5 + (drand48 ());
203 //------------------------------------------------------------------------------
205 static gboolean show_fps = TRUE;
206 static int number_of_frames = 0;
207 static long millis_taken_for_frames = 0;
208 static float debug_scale_factor = 1.0f;
209 static const char *game_over_message = NULL;
211 //------------------------------------------------------------------------------
213 static int cos_table[NUMBER_OF_ROTATION_ANGLES];
214 static int sin_table[NUMBER_OF_ROTATION_ANGLES];
217 init_trigonometric_tables ()
220 int q = (NUMBER_OF_ROTATION_ANGLES / 4);
222 for (i = 0; i < NUMBER_OF_ROTATION_ANGLES; i++)
224 // our angle system is "true north" - 0 is straight up, whereas
225 // cos & sin take 0 as east (and in radians).
226 double angle_in_radians = (q - i) * TWO_PI / NUMBER_OF_ROTATION_ANGLES;
228 +(int) (cos (angle_in_radians) * FIXED_POINT_SCALE_FACTOR);
230 // also, our graphics system is "y axis down", although in regular math,
231 // the y axis is "up", so we have to multiply sin by -1.
233 -(int) (sin (angle_in_radians) * FIXED_POINT_SCALE_FACTOR);
237 //------------------------------------------------------------------------------
240 main (gint argc, gchar ** argv)
244 srand ((unsigned int) time (NULL));
246 init_trigonometric_tables ();
249 gtk_init (&argc, &argv);
251 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
252 g_signal_connect (G_OBJECT (window), "delete-event",
253 G_CALLBACK (gtk_main_quit), NULL);
255 gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);
257 g_signal_connect (G_OBJECT (window), "expose_event",
258 G_CALLBACK (on_expose_event), NULL);
259 g_signal_connect (G_OBJECT (window), "key_press_event",
260 G_CALLBACK (on_key_press), NULL);
261 g_signal_connect (G_OBJECT (window), "key_release_event",
262 G_CALLBACK (on_key_release), NULL);
263 g_timeout_add (MILLIS_PER_FRAME, (GSourceFunc) on_timeout, window);
265 gtk_widget_show_all (window);
271 //------------------------------------------------------------------------------
274 get_time_millis (void)
278 return (long) ((tp.time * 1000) + tp.millitm);
281 //------------------------------------------------------------------------------
284 on_expose_event (GtkWidget * widget, GdkEventExpose * event)
286 cairo_t *cr = gdk_cairo_create (widget->window);
291 start_time = get_time_millis ();
296 scale_for_aspect_ratio (cr, widget->allocation.width,
297 widget->allocation.height);
299 cairo_scale (cr, debug_scale_factor, debug_scale_factor);
301 /* draw background space color */
302 cairo_set_source_rgb (cr, 0.1, 0.0, 0.1);
306 for (i = 0; i < NUMBER_OF_STARS; i++)
309 cairo_translate (cr, stars[i].x, stars[i].y);
310 cairo_rotate (cr, stars[i].rotation);
311 cairo_scale (cr, stars[i].scale, stars[i].scale);
316 // ... the energy bars...
318 cairo_translate (cr, 30, 30);
319 cairo_rotate (cr, 0);
320 draw_energy_bar (cr, &player1);
324 cairo_translate (cr, WIDTH - 30, 30);
325 cairo_rotate (cr, PI);
326 draw_energy_bar (cr, &player2);
329 // ... the two ships...
331 cairo_translate (cr, player1.p.x / FIXED_POINT_SCALE_FACTOR,
332 player1.p.y / FIXED_POINT_SCALE_FACTOR);
333 cairo_rotate (cr, player1.p.rotation * RADIANS_PER_ROTATION_ANGLE);
334 draw_ship_body (cr, &player1);
338 cairo_translate (cr, player2.p.x / FIXED_POINT_SCALE_FACTOR,
339 player2.p.y / FIXED_POINT_SCALE_FACTOR);
340 cairo_rotate (cr, player2.p.rotation * RADIANS_PER_ROTATION_ANGLE);
341 draw_ship_body (cr, &player2);
344 // ... and any missiles.
345 for (i = 0; i < MAX_NUMBER_OF_MISSILES; i++)
347 if (missiles[i].is_alive)
350 cairo_translate (cr, missiles[i].p.x / FIXED_POINT_SCALE_FACTOR,
351 missiles[i].p.y / FIXED_POINT_SCALE_FACTOR);
353 missiles[i].p.rotation * RADIANS_PER_ROTATION_ANGLE);
354 draw_missile (cr, &(missiles[i]));
359 if (game_over_message == NULL)
363 game_over_message = (player2.is_dead) ? "DRAW" : "RED wins";
367 game_over_message = (player2.is_dead) ? "BLUE wins" : NULL;
370 if (game_over_message != NULL)
372 show_text_message (cr, 80, -30, game_over_message);
373 show_text_message (cr, 30, +40, "Press [SPACE] to restart");
381 millis_taken_for_frames += get_time_millis () - start_time;
382 if (number_of_frames >= 100)
385 1000.0 * ((double) number_of_frames) /
386 ((double) millis_taken_for_frames);
387 printf ("%d frames in %ldms (%.3ffps)\n", number_of_frames,
388 millis_taken_for_frames, fps);
389 number_of_frames = 0;
390 millis_taken_for_frames = 0L;
398 //------------------------------------------------------------------------------
401 scale_for_aspect_ratio (cairo_t * cr, int widget_width, int widget_height)
404 int playfield_width, playfield_height;
406 gboolean is_widget_wider;
408 is_widget_wider = (widget_width * HEIGHT) > (WIDTH * widget_height);
412 scale = ((double) widget_height) / HEIGHT;
413 playfield_width = (WIDTH * widget_height) / HEIGHT;
414 playfield_height = widget_height;
415 tx = (widget_width - playfield_width) / 2;
420 scale = ((double) widget_width) / WIDTH;
421 playfield_width = widget_width;
422 playfield_height = (HEIGHT * widget_width) / WIDTH;
424 ty = (widget_height - playfield_height) / 2;
427 cairo_translate (cr, tx, ty);
428 cairo_rectangle (cr, 0, 0, playfield_width, playfield_height);
431 cairo_scale (cr, scale, scale);
434 //------------------------------------------------------------------------------
437 draw_energy_bar (cairo_t * cr, player_t * p)
439 cairo_pattern_t *pat;
444 cairo_rectangle (cr, 0, -5, p->energy / 5, 10);
446 pat = cairo_pattern_create_linear (0, 0, SHIP_MAX_ENERGY / 5, 0);
447 cairo_pattern_add_color_stop_rgba (pat, 0,
448 p->secondary_color.r,
449 p->secondary_color.g,
450 p->secondary_color.b, alpha);
451 cairo_pattern_add_color_stop_rgba (pat, 1, p->primary_color.r,
452 p->primary_color.g, p->primary_color.b,
455 cairo_set_source (cr, pat);
456 cairo_fill_preserve (cr);
457 cairo_pattern_destroy (pat);
459 cairo_set_source_rgb (cr, 0, 0, 0);
464 //------------------------------------------------------------------------------
467 draw_ship_body (cairo_t * cr, player_t * p)
469 cairo_pattern_t *pat;
473 cairo_set_source_rgba (cr, p->primary_color.r, p->primary_color.g,
474 p->primary_color.b, 0.5);
475 cairo_arc (cr, 0, 0, SHIP_RADIUS / FIXED_POINT_SCALE_FACTOR, 0, TWO_PI);
480 cairo_scale (cr, GLOBAL_SHIP_SCALE_FACTOR, GLOBAL_SHIP_SCALE_FACTOR);
487 draw_flare (cr, p->primary_color);
490 if (p->is_turning_left && !p->is_turning_right)
492 draw_turning_flare (cr, p->primary_color, -1.0);
495 if (!p->is_turning_left && p->is_turning_right)
497 draw_turning_flare (cr, p->primary_color, 1.0);
501 cairo_move_to (cr, 0, -33);
502 cairo_curve_to (cr, 2, -33, 3, -34, 4, -35);
503 cairo_curve_to (cr, 8, -10, 6, 15, 15, 15);
504 cairo_line_to (cr, 20, 15);
505 cairo_line_to (cr, 20, 7);
506 cairo_curve_to (cr, 25, 10, 28, 22, 25, 28);
507 cairo_curve_to (cr, 20, 26, 8, 24, 0, 24);
509 cairo_curve_to (cr, -8, 24, -20, 26, -25, 28);
510 cairo_curve_to (cr, -28, 22, -25, 10, -20, 7);
511 cairo_line_to (cr, -20, 15);
512 cairo_line_to (cr, -15, 15);
513 cairo_curve_to (cr, -6, 15, -8, -10, -4, -35);
514 cairo_curve_to (cr, -3, -34, -2, -33, 0, -33);
516 pat = cairo_pattern_create_linear (-30.0, -30.0, 30.0, 30.0);
517 cairo_pattern_add_color_stop_rgba (pat, 0,
518 p->primary_color.r, p->primary_color.g,
519 p->primary_color.b, 1);
520 cairo_pattern_add_color_stop_rgba (pat, 1, p->secondary_color.r,
521 p->secondary_color.g,
522 p->secondary_color.b, 1);
524 cairo_set_source (cr, pat);
525 cairo_fill_preserve (cr);
526 cairo_pattern_destroy (pat);
528 cairo_set_source_rgb (cr, 0, 0, 0);
533 //------------------------------------------------------------------------------
536 draw_flare (cairo_t * cr, RGB_t color)
538 cairo_pattern_t *pat;
541 cairo_translate (cr, 0, 22);
542 pat = cairo_pattern_create_radial (0, 0, 2, 0, 5, 12);
544 cairo_pattern_add_color_stop_rgba (pat, 0.0, color.r, color.g, color.b, 1);
545 cairo_pattern_add_color_stop_rgba (pat, 0.3, 1, 1, 1, 1);
546 cairo_pattern_add_color_stop_rgba (pat, 1.0, color.r, color.g, color.b, 0);
547 cairo_set_source (cr, pat);
548 cairo_arc (cr, 0, 0, 20, 0, TWO_PI);
550 cairo_pattern_destroy (pat);
554 //------------------------------------------------------------------------------
557 draw_turning_flare (cairo_t * cr, RGB_t color, int right_hand_side)
559 cairo_pattern_t *pat;
562 cairo_translate (cr, -23 * right_hand_side, 28);
563 pat = cairo_pattern_create_radial (0, 0, 1, 0, 0, 7);
564 cairo_pattern_add_color_stop_rgba (pat, 0.0, 1, 1, 1, 1);
565 cairo_pattern_add_color_stop_rgba (pat, 1.0, color.r, color.g, color.b, 0);
566 cairo_set_source (cr, pat);
567 cairo_arc (cr, 0, 0, 7, 0, TWO_PI);
569 cairo_pattern_destroy (pat);
571 cairo_translate (cr, 42 * right_hand_side, -22);
572 pat = cairo_pattern_create_radial (0, 0, 1, 0, 0, 7);
573 cairo_pattern_add_color_stop_rgba (pat, 0.0, 1, 1, 1, 1);
574 cairo_pattern_add_color_stop_rgba (pat, 1.0, color.r, color.g, color.b, 0);
575 cairo_set_source (cr, pat);
576 cairo_arc (cr, 0, 0, 5, 0, TWO_PI);
578 cairo_pattern_destroy (pat);
583 //------------------------------------------------------------------------------
586 draw_missile (cairo_t * cr, missile_t * m)
589 cairo_scale (cr, GLOBAL_SHIP_SCALE_FACTOR, GLOBAL_SHIP_SCALE_FACTOR);
593 draw_exploded_missile (cr, m);
597 cairo_pattern_t *pat;
599 double alpha = ((double) m->ticks_to_live) / MISSILE_TICKS_TO_LIVE;
600 // non-linear scaling so things don't fade out too fast
601 alpha = 1.0 - (1.0 - alpha) * (1.0 - alpha);
604 cairo_move_to (cr, 0, -4);
605 cairo_curve_to (cr, 3, -4, 4, -2, 4, 0);
606 cairo_curve_to (cr, 4, 4, 2, 10, 0, 18);
608 cairo_curve_to (cr, -2, 10, -4, 4, -4, 0);
609 cairo_curve_to (cr, -4, -2, -3, -4, 0, -4);
611 pat = cairo_pattern_create_linear (0.0, -5.0, 0.0, 5.0);
612 cairo_pattern_add_color_stop_rgba (pat, 0,
615 m->primary_color.b, alpha);
616 cairo_pattern_add_color_stop_rgba (pat, 1, m->secondary_color.r,
617 m->secondary_color.g,
618 m->secondary_color.b, alpha);
620 cairo_set_source (cr, pat);
622 cairo_pattern_destroy (pat);
626 cairo_arc (cr, 0, 0, 3, 0, TWO_PI);
628 pat = cairo_pattern_create_linear (0, 3, 0, -3);
629 cairo_pattern_add_color_stop_rgba (pat, 0,
632 m->primary_color.b, alpha);
633 cairo_pattern_add_color_stop_rgba (pat, 1, m->secondary_color.r,
634 m->secondary_color.g,
635 m->secondary_color.b, alpha);
637 cairo_set_source (cr, pat);
639 cairo_pattern_destroy (pat);
646 //------------------------------------------------------------------------------
649 draw_exploded_missile (cairo_t * cr, missile_t * m)
652 cairo_pattern_t *pat;
655 cairo_scale (cr, GLOBAL_SHIP_SCALE_FACTOR, GLOBAL_SHIP_SCALE_FACTOR);
657 alpha = ((double) m->ticks_to_live) / MISSILE_EXPLOSION_TICKS_TO_LIVE;
658 alpha = 1.0 - (1.0 - alpha) * (1.0 - alpha);
660 cairo_arc (cr, 0, 0, 30, 0, TWO_PI);
662 pat = cairo_pattern_create_radial (0, 0, 0, 0, 0, 30);
663 cairo_pattern_add_color_stop_rgba (pat, 0,
664 m->primary_color.r, m->primary_color.g,
665 m->primary_color.b, alpha);
666 cairo_pattern_add_color_stop_rgba (pat, 0.5, m->secondary_color.r,
667 m->secondary_color.g,
668 m->secondary_color.b, alpha * 0.75);
669 cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 0, 0);
671 cairo_set_source (cr, pat);
673 cairo_pattern_destroy (pat);
677 //------------------------------------------------------------------------------
680 draw_star (cairo_t * cr)
682 int a = NUMBER_OF_ROTATION_ANGLES / 10;
689 cairo_move_to (cr, r1 * cos_table[0] / FIXED_POINT_SCALE_FACTOR,
690 r1 * sin_table[0] / FIXED_POINT_SCALE_FACTOR);
692 for (i = 0; i < 5; i++) {
693 cairo_line_to (cr, r1 * cos_table[0] / FIXED_POINT_SCALE_FACTOR,
694 r1 * sin_table[0] / FIXED_POINT_SCALE_FACTOR);
695 cairo_line_to (cr, r2 * cos_table[a] / FIXED_POINT_SCALE_FACTOR,
696 r2 * sin_table[a] / FIXED_POINT_SCALE_FACTOR);
697 cairo_rotate (cr, 4*a*PI/NUMBER_OF_ROTATION_ANGLES);
700 cairo_close_path (cr);
704 cairo_set_source_rgb (cr, c, c, c);
708 //------------------------------------------------------------------------------
711 on_timeout (gpointer data)
715 player1.is_hit = FALSE;
716 player2.is_hit = FALSE;
718 apply_physics_to_player (&player1);
719 apply_physics_to_player (&player2);
721 if (check_for_collision (&(player1.p), &(player2.p)))
733 enforce_minimum_distance (&(player1.p), &(player2.p));
740 dvx = (p1vx - p2vx) / FIXED_POINT_HALF_SCALE_FACTOR;
741 dvy = (p1vy - p2vy) / FIXED_POINT_HALF_SCALE_FACTOR;
742 dv2 = (dvx * dvx) + (dvy * dvy);
743 damage = ((int)(sqrt (dv2))) / DAMAGE_PER_SHIP_BOUNCE_DIVISOR;
745 player1.energy -= damage;
746 player2.energy -= damage;
747 player1.is_hit = TRUE;
748 player2.is_hit = TRUE;
750 player1.p.vx = (p1vx * -2 / 8) + (p2vx * +5 / 8);
751 player1.p.vy = (p1vy * -2 / 8) + (p2vy * +5 / 8);
752 player2.p.vx = (p1vx * +5 / 8) + (p2vx * -2 / 8);
753 player2.p.vy = (p1vy * +5 / 8) + (p2vy * -2 / 8);
756 for (i = 0; i < MAX_NUMBER_OF_MISSILES; i++)
758 if (missiles[i].is_alive)
760 apply_physics (&(missiles[i].p));
762 if (!missiles[i].has_exploded)
764 if (check_for_collision (&(missiles[i].p), &(player1.p)))
766 on_collision (&player1, &(missiles[i]));
769 if (check_for_collision (&(missiles[i].p), &(player2.p)))
771 on_collision (&player2, &(missiles[i]));
775 missiles[i].ticks_to_live--;
776 if (missiles[i].ticks_to_live <= 0)
778 missiles[i].is_alive = FALSE;
783 if (player1.energy <= 0)
786 player1.is_dead = TRUE;
790 player1.energy = MIN (SHIP_MAX_ENERGY, player1.energy + 1);
793 if (player2.energy <= 0)
796 player2.is_dead = TRUE;
800 player2.energy = MIN (SHIP_MAX_ENERGY, player2.energy + 1);
803 gtk_widget_queue_draw ((GtkWidget *) data);
807 //------------------------------------------------------------------------------
810 apply_physics_to_player (player_t * player)
813 physics_t *p = &(player->p);
815 if (!player->is_dead)
817 // check if player is turning left, ...
818 if (player->is_turning_left)
821 while (p->rotation < 0)
823 p->rotation += NUMBER_OF_ROTATION_ANGLES;
828 if (player->is_turning_right)
831 while (p->rotation >= NUMBER_OF_ROTATION_ANGLES)
833 p->rotation -= NUMBER_OF_ROTATION_ANGLES;
837 // check if accelerating
838 if (player->is_thrusting)
840 p->vx += SHIP_ACCELERATION_FACTOR * cos_table[p->rotation];
841 p->vy += SHIP_ACCELERATION_FACTOR * sin_table[p->rotation];
844 // apply velocity upper bound
845 v2 = ((p->vx) * (p->vx)) + ((p->vy) * (p->vy));
846 m2 = SHIP_MAX_VELOCITY * SHIP_MAX_VELOCITY;
849 p->vx = (int) (((double) (p->vx) * m2) / v2);
850 p->vy = (int) (((double) (p->vy) * m2) / v2);
853 // check if player is shooting
854 if (player->ticks_until_can_fire == 0)
856 if ((player->is_firing) && (player->energy > ENERGY_PER_MISSILE))
858 int xx = cos_table[p->rotation];
859 int yy = sin_table[p->rotation];
861 missile_t *m = &(missiles[next_missile_index++]);
863 player->energy -= ENERGY_PER_MISSILE;
865 if (next_missile_index == MAX_NUMBER_OF_MISSILES)
867 next_missile_index = 0;
873 MISSILE_RADIUS) / FIXED_POINT_SCALE_FACTOR) * xx);
877 MISSILE_RADIUS) / FIXED_POINT_SCALE_FACTOR) * yy);
878 m->p.vx = p->vx + (MISSILE_SPEED * xx);
879 m->p.vy = p->vy + (MISSILE_SPEED * yy);
880 m->p.rotation = p->rotation;
881 m->ticks_to_live = MISSILE_TICKS_TO_LIVE;
882 m->primary_color = player->primary_color;
883 m->secondary_color = player->secondary_color;
885 m->has_exploded = FALSE;
887 player->ticks_until_can_fire += TICKS_BETWEEN_FIRE;
892 player->ticks_until_can_fire--;
896 // apply velocity deltas to displacement
900 //------------------------------------------------------------------------------
903 apply_physics (physics_t * p)
906 while (p->x > (WIDTH * FIXED_POINT_SCALE_FACTOR))
908 p->x -= (WIDTH * FIXED_POINT_SCALE_FACTOR);
912 p->x += (WIDTH * FIXED_POINT_SCALE_FACTOR);
916 while (p->y > (HEIGHT * FIXED_POINT_SCALE_FACTOR))
918 p->y -= (HEIGHT * FIXED_POINT_SCALE_FACTOR);
922 p->y += (HEIGHT * FIXED_POINT_SCALE_FACTOR);
926 //------------------------------------------------------------------------------
929 check_for_collision (physics_t * p1, physics_t * p2)
931 int dx = (p1->x - p2->x) / FIXED_POINT_HALF_SCALE_FACTOR;
932 int dy = (p1->y - p2->y) / FIXED_POINT_HALF_SCALE_FACTOR;
933 int r = (p1->radius + p2->radius) / FIXED_POINT_HALF_SCALE_FACTOR;
934 int d2 = (dx * dx) + (dy * dy);
935 return (d2 < (r * r)) ? TRUE : FALSE;
938 //------------------------------------------------------------------------------
941 enforce_minimum_distance (physics_t * p1, physics_t * p2)
943 int dx = p1->x - p2->x;
944 int dy = p1->y - p2->y;
945 double d2 = (((double) dx) * dx) + (((double) dy) * dy);
946 int d = (int) sqrt (d2);
948 int r = p1->radius + p2->radius;
950 // normalize dx and dy to length = ((r - d) / 2) + fudge_factor
951 int desired_vector_length = ((r - d) * 5) / 8;
953 dx *= desired_vector_length;
954 dy *= desired_vector_length;
964 //------------------------------------------------------------------------------
967 on_collision (player_t * p, missile_t * m)
969 p->energy -= DAMAGE_PER_MISSILE;
971 m->has_exploded = TRUE;
972 m->ticks_to_live = MISSILE_EXPLOSION_TICKS_TO_LIVE;
977 //------------------------------------------------------------------------------
980 show_text_message (cairo_t * cr, int font_size, int dy, const char *message)
983 cairo_text_extents_t extents;
987 cairo_select_font_face (cr, "Serif",
988 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
990 cairo_set_font_size (cr, font_size);
991 cairo_text_extents (cr, message, &extents);
992 x = (WIDTH / 2) - (extents.width / 2 + extents.x_bearing);
993 y = (HEIGHT / 2) - (extents.height / 2 + extents.y_bearing);
995 cairo_set_source_rgba (cr, 1, 1, 1, 1);
996 cairo_move_to (cr, x, y + dy);
997 cairo_show_text (cr, message);
1001 //------------------------------------------------------------------------------
1006 player1.p.x = 200 * FIXED_POINT_SCALE_FACTOR;
1007 player1.p.y = 200 * FIXED_POINT_SCALE_FACTOR;
1010 player1.p.rotation = random () % NUMBER_OF_ROTATION_ANGLES;
1011 player1.p.radius = SHIP_RADIUS;
1012 player1.is_thrusting = FALSE;
1013 player1.is_turning_left = FALSE;
1014 player1.is_turning_right = FALSE;
1015 player1.is_firing = FALSE;
1016 player1.primary_color.r = 0.3;
1017 player1.primary_color.g = 0.5;
1018 player1.primary_color.b = 0.9;
1019 player1.secondary_color.r = 0.1;
1020 player1.secondary_color.g = 0.3;
1021 player1.secondary_color.b = 0.3;
1022 player1.ticks_until_can_fire = 0;
1023 player1.energy = SHIP_MAX_ENERGY;
1024 player1.is_hit = FALSE;
1025 player1.is_dead = FALSE;
1027 player2.p.x = 600 * FIXED_POINT_SCALE_FACTOR;
1028 player2.p.y = 400 * FIXED_POINT_SCALE_FACTOR;
1031 player2.p.rotation = random () % NUMBER_OF_ROTATION_ANGLES;
1032 player2.p.radius = SHIP_RADIUS;
1033 player2.is_thrusting = FALSE;
1034 player2.is_turning_left = FALSE;
1035 player2.is_turning_right = FALSE;
1036 player2.is_firing = FALSE;
1037 player2.primary_color.r = 0.9;
1038 player2.primary_color.g = 0.2;
1039 player2.primary_color.b = 0.3;
1040 player2.secondary_color.r = 0.5;
1041 player2.secondary_color.g = 0.2;
1042 player2.secondary_color.b = 0.3;
1043 player2.ticks_until_can_fire = 0;
1044 player2.energy = SHIP_MAX_ENERGY;
1045 player2.is_hit = FALSE;
1046 player2.is_dead = FALSE;
1048 init_stars_array ();
1049 init_missiles_array ();
1051 game_over_message = NULL;
1054 //------------------------------------------------------------------------------
1057 on_key_press (GtkWidget * widget, GdkEventKey * event)
1059 return on_key_event (widget, event, TRUE);
1062 //------------------------------------------------------------------------------
1065 on_key_release (GtkWidget * widget, GdkEventKey * event)
1067 return on_key_event (widget, event, FALSE);
1070 //------------------------------------------------------------------------------
1073 on_key_event (GtkWidget * widget, GdkEventKey * event, gboolean key_is_on)
1075 switch (event->keyval)
1081 case GDK_bracketleft:
1084 debug_scale_factor /= 1.25f;
1085 printf ("Scale: %f\n", debug_scale_factor);
1088 case GDK_bracketright:
1091 debug_scale_factor *= 1.25f;
1092 printf ("Scale: %f\n", debug_scale_factor);
1097 if (game_over_message != NULL)
1104 player1.is_turning_left = key_is_on;
1107 player1.is_turning_right = key_is_on;
1110 player1.is_thrusting = key_is_on;
1113 player1.is_firing = key_is_on;
1118 player2.is_turning_left = key_is_on;
1122 player2.is_turning_right = key_is_on;
1126 player2.is_thrusting = key_is_on;
1130 player2.is_firing = key_is_on;