1 /*
2   brickout.c
3 
4   A break-the-bricks style game for X11 or SDL, originally made for the
5   Agenda VR3 Linux- and X-Window-based PDA, but also ported to the
6   Sharp Zaurus SL-5500 Linux- and Qtopia-based PDA (using SDL).
7 
8   by Bill Kendrick
9   bill@newbreedsoftware.com
10   http://www.newbreedsoftware.com/brickout/
11 
12   With contributions from Tom Satter
13   tsatter@purecode.com
14 
15   May 17, 2001 - June 9, 2002
16 */
17 
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #ifndef USE_SDL
24 
25 #include <X11/Xlib.h>
26 #include <X11/xpm.h>
27 #include <X11/keysym.h>
28 #include <X11/Xutil.h>
29 #include <X11/Xatom.h>
30 
31 #define XBUTTON xbutton
32 #define SDL_X_OFF 0
33 #define SDL_Y_OFF 0
34 
35 #else
36 
37 #include "SDL.h"
38 #include "SDL_mixer.h"
39 
40 typedef SDL_Surface * Window;
41 typedef SDL_Surface * Pixmap;
42 typedef SDL_Surface * Drawable;
43 typedef int Display;
44 typedef Uint32 GC;
45 typedef SDL_Event XEvent;
46 typedef SDLKey KeySym;
47 
48 #define XBUTTON button
49 
50 #define ButtonPress SDL_MOUSEBUTTONDOWN
51 #define MotionNotify SDL_MOUSEMOTION
52 #define KeyPress SDL_KEYDOWN
53 #define KeyRelease SDL_KEYUP
54 
55 #define XK_Left SDLK_LEFT
56 #define XK_Right SDLK_RIGHT
57 #define XK_Page_Up SDLK_UNKNOWN
58 #define XK_Page_Down SDLK_UNKNOWN
59 #define XK_space SDLK_SPACE
60 #define XK_Escape SDLK_ESCAPE
61 
62 #define SDL_X_OFF 40
63 #define SDL_Y_OFF 40
64 
65 #endif
66 
67 #include <unistd.h>
68 #include <sys/time.h>
69 
70 #ifndef USE_SDL
71 
72 #include IMAGEDIR "ball.xpm"
73 #include IMAGEDIR "brick1.xpm"
74 #include IMAGEDIR "brick2.xpm"
75 #include IMAGEDIR "brick3.xpm"
76 #include IMAGEDIR "brick4.xpm"
77 #include IMAGEDIR "brick5.xpm"
78 #include IMAGEDIR "conton.xpm"
79 #include IMAGEDIR "contoff.xpm"
80 #include IMAGEDIR "done.xpm"
81 #include IMAGEDIR "launchoff.xpm"
82 #include IMAGEDIR "launchon.xpm"
83 #include IMAGEDIR "leftpipe.xpm"
84 #include IMAGEDIR "numbers.xpm"
85 #include IMAGEDIR "paddle.xpm"
86 #include IMAGEDIR "pause.xpm"
87 #include IMAGEDIR "soundoff.xpm"
88 #include IMAGEDIR "soundon.xpm"
89 #include IMAGEDIR "negoff.xpm"
90 #include IMAGEDIR "negon.xpm"
91 #include IMAGEDIR "posoff.xpm"
92 #include IMAGEDIR "poson.xpm"
93 #include IMAGEDIR "start.xpm"
94 #include IMAGEDIR "stats.xpm"
95 #include IMAGEDIR "stats_leftpipe.xpm"
96 #include IMAGEDIR "stats_rightpipe.xpm"
97 #include IMAGEDIR "stats_toppipe.xpm"
98 #include IMAGEDIR "title.xpm"
99 #include IMAGEDIR "water.xpm"
100 
101 #else
102 
103 const char
104   * ball_xpm = IMAGEDIR "ball.bmp",
105   * brick1_xpm = IMAGEDIR "brick1.bmp",
106   * brick2_xpm = IMAGEDIR "brick2.bmp",
107   * brick3_xpm = IMAGEDIR "brick3.bmp",
108   * brick4_xpm = IMAGEDIR "brick4.bmp",
109   * brick5_xpm = IMAGEDIR "brick5.bmp",
110   * conton_xpm = IMAGEDIR "conton.bmp",
111   * contoff_xpm = IMAGEDIR "contoff.bmp",
112   * done_xpm = IMAGEDIR "done.bmp",
113   * launchoff_xpm = IMAGEDIR "launchoff.bmp",
114   * launchon_xpm = IMAGEDIR "launchon.bmp",
115   * leftpipe_xpm = IMAGEDIR "leftpipe.bmp",
116   * numbers_xpm = IMAGEDIR "numbers.bmp",
117   * paddle_xpm = IMAGEDIR "paddle.bmp",
118   * pause_xpm = IMAGEDIR "pause.bmp",
119   * soundoff_xpm = IMAGEDIR "soundoff.bmp",
120   * soundon_xpm = IMAGEDIR "soundon.bmp",
121   * musicoff_xpm = IMAGEDIR "musicoff.bmp",
122   * musicon_xpm = IMAGEDIR "musicon.bmp",
123   * negoff_xpm = IMAGEDIR "negoff.bmp",
124   * negon_xpm = IMAGEDIR "negon.bmp",
125   * posoff_xpm = IMAGEDIR "posoff.bmp",
126   * poson_xpm = IMAGEDIR "poson.bmp",
127   * start_xpm = IMAGEDIR "start.bmp",
128   * stats_xpm = IMAGEDIR "stats.bmp",
129   * stats_leftpipe_xpm = IMAGEDIR "stats_leftpipe.bmp",
130   * stats_rightpipe_xpm = IMAGEDIR "stats_rightpipe.bmp",
131   * stats_toppipe_xpm = IMAGEDIR "stats_toppipe.bmp",
132   * title_xpm = IMAGEDIR "title.bmp",
133   * water_xpm = IMAGEDIR "water.bmp";
134 
135 #endif
136 
137 
138 /* Globals: */
139 
140 Display *display;
141 Window window;
142 GC whitegc, blackgc, pastegc;
143 
144 Pixmap pix_title, pix_leftpipe;
145 Pixmap pix_stats, pix_stats_toppipe, pix_stats_leftpipe;
146 Pixmap pix_stats_rightpipe, pix_water;
147 Pixmap pix_brick1, pix_brick2, pix_brick3, pix_brick4, pix_brick5;
148 Pixmap pix_done, pix_start, pix_soundon, pix_soundoff, pix_conton, pix_contoff,
149   pix_negon, pix_negoff, pix_poson, pix_posoff;
150 Pixmap pix_pause, pix_launchon, pix_launchoff, pix_numbers;
151 Pixmap pix_ball, pix_paddle;
152 
153 #ifdef USE_SDL
154 Pixmap pix_musicon, pix_musicoff;
155 #endif
156 
157 int score, balls, level, paddle_x, paddle_target_x;
158 int ball_launched, ball_bx, ball_by;
159 int dir_x, dir_y, brake, slope;
160 int redraw_brick_x, redraw_brick_y;
161 
162 char cur_board[6][10];
163 int num_bricks_left;
164 
165 
166 #ifdef USE_SDL
167 
168 typedef struct breaking_brick_type {
169   int time;
170   char kind;
171 } breaking_brick_type;
172 
173 breaking_brick_type breaking_bricks[6][10];
174 
175 
176 
177 #define NUM_GAME_MUSICS 6
178 
179 char * music_fnames[NUM_GAME_MUSICS] = {
180   MUSICDIR "yawny.mod",
181   MUSICDIR "Bootmusic_9.mod",
182   MUSICDIR "Dj_bobo_die.mod",
183   MUSICDIR "Pleasure_to_paso.mod",
184   MUSICDIR "invasion.mod",
185   MUSICDIR "Valley_of_chip_6.mod"
186 };
187 
188 int use_sound;
189 Mix_Chunk * snd_wall, * snd_ceiling, * snd_metal, * snd_brick,
190 	* snd_unbreakable, * snd_paddle, * snd_die;
191 Mix_Music * mus_title, * mus_game[NUM_GAME_MUSICS];
192 
193 #endif
194 
195 
196 
197 int sound, game_pending;
198 
199 #ifdef USE_SDL
200 int music;
201 #endif
202 
203 int slopes_x[7] = {
204      0,
205    392,
206    800,
207   1144,
208   1448,
209   1720,
210   1896
211 };
212 
213 int slopes_y[7] = {
214   2048,
215   2008,
216   1888,
217   1696,
218   1448,
219   1112,
220    768
221 };
222 
223 /* Valid symbols:
224    "-" - Regular brick
225    "=" - Shiny, two-hit brick
226    "/" - Gradient brick
227    "#" - Bevelled brick
228    ":" - Indestructable rivetted brick
229 */
230 
231 #define NUM_LEVELS 20
232 
233 char boards[NUM_LEVELS][6][10] = {
234   {
235     /* Level 1 */
236     "          ",
237     "----------",
238     "----------",
239     "----------",
240     "          ",
241     "          "
242   },
243   {
244     /* Level 2 */
245     "----------",
246     "----------",
247     "----------",
248     "----------",
249     "==========",
250     "::------::"
251   },
252   {
253     /* Level 3 */
254     "----------",
255     "----------",
256     "---::::---",
257     "---::::---",
258     "----------",
259     "=========="
260   },
261   {
262     /* Level 4 */
263     "==========",
264     "  ::::::  ",
265     "----------",
266     "  ::--::  ",
267     "----------",
268     "  --::--  "
269   },
270   { /* Level 5 */
271     "=-/####/-=",
272     "-=-/##/-=-",
273     "/-=-//-=-/",
274     " /-=--=-/ ",
275     "  /-==-/  ",
276     "   /--/   "
277   },
278   { /* Level 6 */
279     "##########",
280     "==========",
281     "##########",
282     "          ",
283     "::::  ::::",
284     "::      ::"
285   },
286   { /* Level 7 */
287     "#/#/#/#/#/",
288     "/#/#/#/#/#",
289     "#/#/#/#/#/",
290     "/#/#/#/#/#",
291     "          ",
292     "          "
293   },
294   { /* Level 8 */
295     "  /####/  ",
296     " /##//##/ ",
297     " //####// ",
298     " /##//##/ ",
299     "  /####/  ",
300     "          "
301   },
302   { /* Level 9 */
303     "//////////",
304     "  //////  ",
305     "   ----   ",
306     "##:    :##",
307     "#:      :#",
308     ":        :"
309   },
310   { /* Level 10 */
311     "          ",
312     " // // // ",
313     "=-=-=-=-=-",
314     "-=-=-=-=-=",
315     "          ",
316     "==--==--=="
317   },
318   { /* Level 11 */
319     "::      ::",
320     "          ",
321     "  ::::::  ",
322     "          ",
323     "=        =",
324     "::: :: :::"
325   },
326   { /* Level 12 */
327     "          ",
328     "//////////",
329     "####::####",
330     "==========",
331     "          ",
332     " -- -- -- "
333   },
334   { /* Level 13 */
335     "   ####   ",
336     "    ==##  ",
337     "      =## ",
338     "      =## ",
339     "    ==##  ",
340     "   ####   "
341   },
342   { /* Level 14 */
343     "::::::::::",
344     "::======::",
345     ":========:",
346     ":===  ===:",
347     ":==    ==:",
348     "::= == =::"
349   },
350   { /* Level 15 */
351     "/########/",
352     "##########",
353     "#=      =#",
354     "#=      =#",
355     "##======##",
356     "/########/"
357   },
358   { /* Level 16 */
359     "  =    =  ",
360     "   =  =   ",
361     ":   ##   :",
362     ":   ##   :",
363     "   =  =   ",
364     "  =    =  "
365   },
366   { /* Level 17 */
367     "    ==    ",
368     "  ==--==  ",
369     "==--//--==",
370     "--//##//--",
371     "//##  ##//",
372     "##      ##"
373   },
374   { /* Level 18 */
375     "          ",
376     "   ====   ",
377     "  ######  ",
378     "    ==    ",
379     " =      = ",
380     " =      = "
381   },
382   { /* Level 19 */
383     ":########:",
384     ":########:",
385     "///:==:///",
386     "///:==:///",
387     ":--------:",
388     ":--------:"
389   },
390   { /* Level 20 */
391     "//////////",
392     "==========",
393     "##########",
394     "          ",
395     "          ",
396     ": :::::: :"
397   }
398 };
399 
400 
401 /* Local function prototypes: */
402 
403 void setup(void);
404 void loadsettings(void);
405 int title(void);
406 void draw_titlescreen(void);
407 void game(void);
408 void draw_gamescreen(void);
409 #ifndef USE_SDL
410 GC CreateGC(Display *display, Drawable drawable, unsigned long forecolor,
411             unsigned long backcolor);
412 void LoadImage(char ** xpm, Pixmap * pix);
413 #else
414 Pixmap LoadImage(const char * file);
415 #endif
416 void DrawImage(Pixmap pix, Drawable dest, int x, int y, int w, int h);
417 void DrawNumbers(char * str, int x, int y);
418 #ifndef USE_SDL
419 void playsound(int pitch, int duration);
420 #else
421 void playsound(Mix_Chunk * snd);
422 #endif
423 void launch_ball(void);
424 void add_score(int val);
425 void draw_brick(int x, int y);
426 void reset_level(void);
427 int  hit_brick(int x,int y);
428 #ifdef USE_SDL
429 Mix_Chunk * LoadSound(char * fname);
430 #endif
431 
432 
433 /* --- MAIN! --- */
434 
main(int argc,char * argv[])435 int main(int argc, char * argv[])
436 {
437   int done;
438 
439 
440   setup();
441 
442   loadsettings();
443 
444   level = 1;
445   do
446     {
447       done = title();
448 
449       if (!done)
450 	game();
451     }
452   while (!done);
453 
454 #ifdef USE_SDL
455   SDL_Quit();
456 #endif
457 
458   return(0);
459 }
460 
461 
462 /* Title: */
463 
title(void)464 int title(void)
465 {
466   int done, quit, want_level_chg, bx, by;
467   XEvent event;
468   KeySym key;
469   char str[24];
470 #ifdef USE_SDL
471   Uint32 last_tick, cur_tick;
472 #endif
473 
474 
475   draw_titlescreen();
476 
477 
478   /* Title loop: */
479 
480   done = 0;
481   quit = 0;
482 
483 #ifdef USE_SDL
484   last_tick = SDL_GetTicks();
485 #endif
486 
487 
488   do
489     {
490       want_level_chg = 0;
491 
492 #ifndef USE_SDL
493       while (XPending(display))
494 	{
495 	  XNextEvent(display, &event);
496 #else
497       while (SDL_PollEvent(&event) > 0)
498 	{
499 #endif
500 
501 	  if (event.type == ButtonPress)
502 	    {
503 	      bx = event.XBUTTON.x - SDL_X_OFF;
504 	      by = event.XBUTTON.y - SDL_Y_OFF;
505 
506 	      if (bx >= 64 &&
507 		  bx <= 96 &&
508 		  by >= 170 &&
509 		  by <= 183)
510 		{
511 		  /* Start! */
512 
513 		  game_pending = 0;
514 		  done = 1;
515 		}
516 	      else if (bx >= 64 &&
517 		  bx <= 96 &&
518 		  by >= 190 &&
519 		  by <= 203 &&
520 		  game_pending == 1)
521 		{
522 		  /* Continue! */
523 
524 		  done = 1;
525 		}
526 	      else if (bx >= 14 &&
527 		  bx <= 46 &&
528 		  by >= 225 &&
529 		  by <= 238)
530 		{
531 		  /* Done button! */
532 
533 		  done = 1;
534 		  quit = 1;
535 		}
536 	      else if (bx >= 126 &&
537 		       bx <= 144 &&
538 		       by >= 225 &&
539 		       by <= 238)
540 		{
541 		  /* Sound control! */
542 
543 		  sound = !sound;
544 
545 		  if (sound == 1)
546 		    DrawImage(pix_soundon, window, 126, 225, 18, 13);
547 		  else
548 		    DrawImage(pix_soundoff, window, 126, 225, 18, 13);
549 
550 #ifndef USE_SDL
551 		  XFlush(display);
552 #endif
553 		}
554 #ifdef USE_SDL
555 	      else if (bx >= 100 &&
556 		       bx <= 118 &&
557 		       by >= 225 &&
558 		       by <= 238)
559 		{
560 		  /* Music control! */
561 
562 		  music = !music;
563 
564 		  if (music == 1)
565 		    DrawImage(pix_musicon, window, 100, 225, 18, 13);
566 		  else
567 		  {
568 		    DrawImage(pix_musicoff, window, 100, 225, 18, 13);
569 		    Mix_HaltMusic();
570 		  }
571 		}
572 #endif
573 	      else if (bx >= 60 &&
574 		       bx <= 76 &&
575 		       by >= 144 &&
576 		       by <= 157)
577 	        {
578 		  /* Negative button: */
579 
580 	          want_level_chg = -1;
581 	        }
582 	      else if (bx >= 98 &&
583 		       bx <= 114 &&
584 		       by >= 144 &&
585 		       by <= 157)
586 	        {
587 		  /* Positive button: */
588 
589 		  want_level_chg = 1;
590 	        }
591 	    }
592 	  else if (event.type == KeyPress)
593             {
594 #ifndef USE_SDL
595 	      key = XLookupKeysym((XKeyEvent *)&event, 0);
596 #else
597 	      key = event.key.keysym.sym;
598 #endif
599 
600 	      if (key == XK_Left)
601 		want_level_chg = -1;
602 	      else if (key == XK_Right)
603 		want_level_chg = 1;
604 	      else if (key == XK_space)
605 		done = 1;
606 	      else if (key == XK_Escape)
607 	      {
608 		quit = 1;
609 		done = 1;
610 	      }
611 	    }
612 #ifndef USE_SDL
613 	  else if (event.type == Expose)
614 	    {
615 	      draw_titlescreen();
616 	    }
617 #endif
618 	}
619 
620 
621       if (want_level_chg == -1 && level > 1)
622       {
623         level--;
624 
625         sprintf(str, "%.2d", level);
626         DrawNumbers(str, 79, 141);
627 
628         if (level == 1)
629           DrawImage(pix_negoff, window, 60, 144, 16, 13);
630 
631         if (level < NUM_LEVELS)
632           DrawImage(pix_poson, window, 98, 144, 16, 13);
633 
634         game_pending = 0;
635         DrawImage(pix_contoff, window, 64, 190, 32, 13);
636 
637 #ifdef USE_SDL
638 	playsound(snd_wall);
639 #endif
640       }
641       else if (want_level_chg == 1 && level < NUM_LEVELS)
642       {
643         level++;
644 
645         sprintf(str, "%.2d", level);
646         DrawNumbers(str, 79, 141);
647 
648         if (level == NUM_LEVELS)
649           DrawImage(pix_posoff, window, 98, 144, 16, 13);
650 
651         if (level > 1)
652           DrawImage(pix_negon, window, 60, 144, 16, 13);
653 
654         game_pending = 0;
655         DrawImage(pix_contoff, window, 64, 190, 32, 13);
656 
657 #ifdef USE_SDL
658 	playsound(snd_paddle);
659 #endif
660       }
661 
662 
663 #ifdef USE_SDL
664       if (use_sound && music)
665       {
666 	if (!Mix_PlayingMusic())
667 	{
668 	  Mix_PlayMusic(mus_title, -1);
669 	}
670       }
671 #endif
672 
673 
674 #ifdef USE_SDL
675       cur_tick = SDL_GetTicks();
676 
677       if (cur_tick > last_tick + 100)
678       {
679         SDL_Flip(window);
680 	last_tick = cur_tick;
681       }
682 #endif
683     }
684   while (done == 0);
685 
686 #ifdef USE_SDL
687 
688   if (use_sound)
689   {
690     Mix_HaltMusic();
691   }
692 
693   if (quit)
694   {
695     SDL_FillRect(window, NULL, SDL_MapRGB(window->format, 0, 0, 128));
696     SDL_Flip(window);
697     playsound(snd_die);
698     SDL_Delay(1000);
699   }
700 #endif
701 
702   return(quit);
703 }
704 
705 
706 
707 /* Draw title screen: */
708 
709 void draw_titlescreen(void)
710 {
711   int y;
712   char str[20];
713 
714   /* (Title) */
715 
716 #ifndef USE_SDL
717   XFillRectangle(display, window, blackgc, 0, 0, 160, 240);
718 #else
719   SDL_FillRect(window, NULL, blackgc);
720 #endif
721 
722   DrawImage(pix_title, window, 0, 0, 160, 100);
723   for (y = 100; y < 240; y = y + 10)
724     {
725       DrawImage(pix_leftpipe, window, 0, y, 14, 10);
726     }
727 
728 
729   /* (Level buttons) */
730 
731   if (level == 1)
732     DrawImage(pix_negoff, window, 60, 144, 16, 13);
733   else
734     DrawImage(pix_negon, window, 60, 144, 16, 13);
735 
736   sprintf(str, "%.2d", level);
737   DrawNumbers(str, 79, 141);
738 
739   if (level < NUM_LEVELS)
740     DrawImage(pix_poson, window, 98, 144, 16, 13);
741   else
742     DrawImage(pix_posoff, window, 98, 144, 16, 13);
743 
744 
745   /* (Done button) */
746 
747   DrawImage(pix_done, window, 14, 225, 32, 13);
748 
749 
750   /* (Sound control button) */
751 
752   if (sound == 1)
753     DrawImage(pix_soundon, window, 126, 225, 18, 13);
754   else
755     DrawImage(pix_soundoff, window, 126, 225, 18, 13);
756 
757 
758 #ifdef USE_SDL
759   /* (Music control button) */
760 
761   if (music == 1)
762     DrawImage(pix_musicon, window, 100, 225, 18, 13);
763   else
764     DrawImage(pix_musicoff, window, 100, 225, 18, 13);
765 #endif
766 
767 
768   /* (Start button) */
769 
770   DrawImage(pix_start, window, 64, 170, 32, 13);
771 
772 
773   /* (Continue button) */
774 
775   if (game_pending == 1)
776     DrawImage(pix_conton, window, 64, 190, 32, 13);
777   else
778     DrawImage(pix_contoff, window, 64, 190, 32, 13);
779 
780 
781 #ifndef USE_SDL
782   XFlush(display);
783 #endif
784 }
785 
786 
787 /* GAME FUNCTION! */
788 
789 void game(void)
790 {
791   XEvent event;
792   KeySym key;
793   int done, counter;
794   int x, y, i;
795   int ball_x, ball_y, mx, my, hit;
796   int left_down, right_down, bx, by;
797   char brick;
798   struct timeval now, then;
799   long time_padding;
800 #ifdef USE_SDL
801   SDL_Rect dest;
802 #endif
803 
804 
805   /* Set up game: */
806 
807   if (!game_pending)
808     {
809       score = 0;
810       balls = 5;
811       paddle_x = 64;
812       paddle_target_x = 64;
813 
814       for (y = 0; y < 6; y++)
815       {
816 	for (x = 0; x < 10; x++)
817 	{
818 	  cur_board[y][x] = ' ';
819 	}
820       }
821 
822       reset_level();
823 
824       ball_launched  = 0;
825       ball_bx        = 4864;
826       ball_by        = 7424;
827       brake          = 8;
828       slope          = 1;
829       dir_x          = 0;
830       dir_y          = 0;
831       redraw_brick_x = -1;
832       game_pending   = 1;
833     }
834 
835 
836   /* Draw the game screen: */
837 
838   draw_gamescreen();
839 
840   done = 0;
841   left_down = 0;
842   right_down = 0;
843   counter = 0;
844 
845   ball_x = ball_bx >> 6;
846   ball_y = ball_by >> 6;
847   do
848     {
849       gettimeofday(&then, NULL);
850       counter++;
851 
852       /* Erase paddle: */
853 
854 #ifndef USE_SDL
855       XFillRectangle(display, window, whitegc, paddle_x, 150, 32, 10);
856 #else
857       dest.x = paddle_x + SDL_X_OFF;
858       dest.y = 150 + SDL_Y_OFF;
859       dest.w = 32;
860       dest.h = 10;
861 
862       SDL_FillRect(window, &dest, whitegc);
863 #endif
864 
865 
866       /* Erase ball: */
867 
868 #ifndef USE_SDL
869       XFillRectangle(display, window, whitegc, ball_x, ball_y, 8, 8);
870 #else
871       dest.x = ball_x + SDL_X_OFF;
872       dest.y = ball_y + SDL_Y_OFF;
873       dest.w = 8;
874       dest.h = 8;
875 
876       SDL_FillRect(window, &dest, whitegc);
877 #endif
878 
879       if (redraw_brick_x >= 0)
880         {
881           draw_brick(redraw_brick_x, redraw_brick_y);
882           redraw_brick_x = -1;
883         }
884 
885       /* Handle events: */
886 
887 #ifndef USE_SDL
888       while (XPending(display))
889 	{
890 	  XNextEvent(display, &event);
891 #else
892       while (SDL_PollEvent(&event) > 0)
893 	{
894 #endif
895 
896 	  if (event.type == ButtonPress)
897 	    {
898 	      bx = event.XBUTTON.x - SDL_X_OFF;
899 	      by = event.XBUTTON.y - SDL_Y_OFF;
900 
901 	      if (bx >= 14 &&
902 		  bx <= 46 &&
903 		  by >= 225 &&
904 		  by <= 238)
905 		{
906 		  /* Pause button! */
907 
908 		  done = 1;
909 		}
910 	      else if (bx >= 64 &&
911 		       bx <= 96 &&
912 		       by >= 225 &&
913 		       by <= 238)
914 		{
915 		  /* Ball launcher button! */
916 
917 		  if (ball_launched == 0)
918 		    launch_ball();
919 		}
920 	      else if (bx >= 126 &&
921 		       bx <= 144 &&
922 		       by >= 225 &&
923 		       by <= 238)
924 		{
925 		  /* Sound control! */
926 
927 		  sound = !sound;
928 
929 		  if (sound == 1)
930 		    DrawImage(pix_soundon, window, 126, 225, 18, 13);
931 		  else
932 		    DrawImage(pix_soundoff, window, 126, 225, 18, 13);
933 
934 #ifndef USE_SDL
935 		  XFlush(display);
936 #endif
937 		}
938 #ifdef USE_SDL
939 	      else if (bx >= 100 &&
940 		       bx <= 118 &&
941 		       by >= 225 &&
942 		       by <= 238)
943 		{
944 		  /* Music control! */
945 
946 		  music = !music;
947 
948 		  if (music == 1)
949 		    DrawImage(pix_musicon, window, 100, 225, 18, 13);
950 		  else
951 		  {
952 		    DrawImage(pix_musicoff, window, 100, 225, 18, 13);
953 		    Mix_HaltMusic();
954 		  }
955 		}
956 #endif
957 	      else if (by < 160)
958 		{
959 		  /* Tap screen elsewhere - move paddle! */
960 		  paddle_target_x = bx - 16;
961 
962 		  if (paddle_target_x < -16)
963 		    paddle_target_x = -16;
964 		  else if (paddle_target_x > 144)
965 		    paddle_target_x = 144;
966 		}
967 	    }
968 	  else if (event.type == MotionNotify)
969             {
970 	      bx = event.XBUTTON.x - SDL_X_OFF;
971 	      by = event.XBUTTON.y - SDL_Y_OFF;
972 
973 	      /* Move stylus - move paddle? */
974               paddle_target_x = bx - 16;
975 
976               if (paddle_target_x < -16)
977                 paddle_target_x = -16;
978               else if (paddle_target_x > 144)
979                 paddle_target_x = 144;
980             }
981 	  else if (event.type == KeyPress)
982 	    {
983 #ifndef USE_SDL
984 	      /* Key press! */
985 
986 	      key = XLookupKeysym((XKeyEvent *)&event, 0);
987 #else
988 	      key = event.key.keysym.sym;
989 #endif
990 
991 	      if (key == XK_Left)
992 		{
993 		  /* Left */
994 
995 		  left_down = 1;
996 		  right_down = 0;
997 		}
998 	      else if (key == XK_Right)
999 		{
1000 		  /* Right */
1001 
1002 		  left_down = 0;
1003 		  right_down = 1;
1004 		}
1005 	      else if (key == XK_Page_Up || key == XK_Page_Down ||
1006 		       key == XK_space)
1007 		{
1008 		  /* PageUp/PageDown */
1009 
1010 		  if (ball_launched == 0)
1011 		    launch_ball();
1012 		}
1013 	      else if (key == XK_Escape)
1014 		{
1015 		  done = 1;
1016 		}
1017 	    }
1018 	  else if (event.type == KeyRelease)
1019 	    {
1020 #ifndef USE_SDL
1021 	      /* Key release! */
1022 
1023 	      key = XLookupKeysym((XKeyEvent *)&event, 0);
1024 #else
1025 	      key = event.key.keysym.sym;
1026 #endif
1027 
1028 	      if (key == XK_Left)
1029 		{
1030 		  left_down = 0;
1031 		}
1032 	      else if (key == XK_Right)
1033 		{
1034 		  right_down = 0;
1035 		}
1036 	    }
1037 #ifndef USE_SDL
1038 	  else if (event.type == Expose)
1039 	    {
1040 	      draw_gamescreen();
1041 	    }
1042 #endif
1043 	}
1044 
1045 
1046       /* Move paddle based on keypresses: */
1047 
1048       if (left_down)
1049 	{
1050 	  paddle_x = paddle_x - 4;
1051 	  if (paddle_x < -16)
1052 	    paddle_x = -16;
1053           paddle_target_x = paddle_x;
1054 	}
1055       else if (right_down)
1056 	{
1057 	  paddle_x = paddle_x + 4;
1058 	  if (paddle_x > 144)
1059 	    paddle_x = 144;
1060           paddle_target_x = paddle_x;
1061 	}
1062 
1063       if (paddle_target_x > paddle_x)
1064         {
1065           if (paddle_target_x > paddle_x + 4)
1066             paddle_x += 4;
1067           else
1068             paddle_x = paddle_target_x;
1069         }
1070       else if (paddle_target_x < paddle_x)
1071         {
1072           if (paddle_target_x < paddle_x - 4)
1073             paddle_x -= 4;
1074           else
1075             paddle_x = paddle_target_x;
1076         }
1077 
1078       /* Control ball: */
1079 
1080       if (ball_launched)
1081 	{
1082           /* Move the ball: */
1083 
1084           ball_bx += dir_x * (slopes_x[slope] / (brake + 8));
1085           ball_by += dir_y * (slopes_y[slope] / (brake + 8));
1086           ball_x   = ball_bx >> 6;
1087           ball_y   = ball_by >> 6;
1088 
1089           /* Bounce off sides: */
1090 
1091           if (ball_x < 0)
1092             {
1093               ball_x = 0;
1094               dir_x = 1;
1095 
1096 #ifndef USE_SDL
1097               playsound(500, 10);
1098 #else
1099 	      playsound(snd_wall);
1100 #endif
1101             }
1102           else if (ball_x > 152)
1103             {
1104               ball_x = 152;
1105               dir_x = -1;
1106 
1107 #ifndef USE_SDL
1108               playsound(500, 10);
1109 #else
1110 	      playsound(snd_wall);
1111 #endif
1112             }
1113 
1114 	  /* Bounce off top of screen: */
1115 
1116 	  if (ball_y < 0)
1117 	    {
1118 	      ball_y = 0;
1119 	      dir_y = 1;
1120 
1121 	      /* Go faster! */
1122 
1123               brake--;
1124               if (brake < 0)
1125                 brake = 0;
1126 
1127 #ifndef USE_SDL
1128 	      playsound(3000, 10);
1129 #else
1130 	      playsound(snd_ceiling);
1131 #endif
1132 	    }
1133 
1134 	  /* Bounce off bricks: */
1135 
1136 	  if (ball_y < 60)
1137 	    {
1138 	      x = (ball_x + 4) >> 4;
1139 	      y = (ball_y + 4) / 10;
1140               mx = (ball_x + 4) % 16;
1141               my = (ball_y + 4) % 10;
1142 
1143               hit = 0;
1144               if (y < 6)
1145                 {
1146                   hit = hit_brick(x,y);
1147                   if (!hit)
1148                     {
1149                       if (mx + 4 > 15 && x < 9)
1150                         {
1151                           x++;
1152                           hit = hit_brick(x,y);
1153                           if (!hit)
1154                             x--;
1155                         }
1156                       else if (mx - 4 < 0 && x > 0)
1157                         {
1158                           x--;
1159                           hit = hit_brick(x,y);
1160                           if (!hit)
1161                             x++;
1162                         }
1163                     }
1164                 }
1165               if (!hit)
1166                 {
1167                   if (my + 4 > 9 && y < 5)
1168                     {
1169                       y++;
1170                       hit = hit_brick(x,y);
1171                       if (!hit)
1172                         {
1173                           if (mx + 4 > 15 && x < 9)
1174                             {
1175                               x++;
1176                               hit = hit_brick(x,y);
1177                               if (!hit)
1178                                 x--;
1179                             }
1180                           else if (mx - 4 < 0 && x > 0)
1181                             {
1182                               x--;
1183                               hit = hit_brick(x,y);
1184                               if (!hit)
1185                                 x++;
1186                             }
1187                         }
1188                     }
1189                   else if (my - 4 < 0 && y > 0)
1190                     {
1191                       y--;
1192                       hit = hit_brick(x,y);
1193                       if (!hit)
1194                         {
1195                           if (mx + 4 > 15 && x < 9)
1196                             {
1197                               x++;
1198                               hit = hit_brick(x,y);
1199                             }
1200                           else if (mx - 4 < 0 && x > 0)
1201                             {
1202                               x--;
1203                               hit = hit_brick(x,y);
1204                             }
1205                         }
1206                     }
1207                 }
1208 
1209 	      if (hit)
1210 		{
1211                   ball_x = ball_bx >> 6;
1212                   ball_y = ball_by >> 6;
1213 
1214                   brick = cur_board[y][x];
1215 		  if (brick == '=')
1216 		    {
1217 		      brick = '-';
1218 		      add_score(10);
1219 
1220 #ifndef USE_SDL
1221 		      playsound(2000, 10);
1222 #else
1223 		      playsound(snd_metal);
1224 #endif
1225 
1226 #ifdef USE_SDL
1227 		      breaking_bricks[y][x].time = 10;
1228 		      breaking_bricks[y][x].kind = cur_board[y][x];
1229 #endif
1230 		    }
1231 		  else if (brick == '-' || brick == '/' ||
1232 			   brick == '#')
1233 		    {
1234 		      add_score(5);
1235 		      brick = ' ';
1236 
1237 		      num_bricks_left--;
1238 
1239 #ifndef USE_SDL
1240 		      playsound(750, 10);
1241 #else
1242 		      playsound(snd_brick);
1243 #endif
1244 
1245 #ifdef USE_SDL
1246 		      breaking_bricks[y][x].time = 10;
1247 		      breaking_bricks[y][x].kind = cur_board[y][x];
1248 #endif
1249 		    }
1250 		  else
1251 		    {
1252 #ifndef USE_SDL
1253 		      playsound(100, 10);
1254 #else
1255 		      playsound(snd_unbreakable);
1256 #endif
1257 		    }
1258 
1259 
1260 		  cur_board[y][x] = brick;
1261 
1262 
1263 		  /* Erase (or redraw) brick: */
1264 
1265 		  draw_brick(x, y);
1266 		}
1267 	    }
1268 
1269 
1270 	  /* Bounce off the paddle: */
1271 
1272 	  if (ball_y >= 142 &&
1273 	      ball_x >= paddle_x - 8 &&
1274 	      ball_x <= paddle_x + 32)
1275 	    {
1276               dir_y = -1;
1277               counter = 0;
1278 
1279 	      /* Angle (xm) depends on where it hit the paddle: */
1280 
1281 	      if (ball_x <= paddle_x - 4)
1282 		slope -= dir_x * 3;
1283 	      if (ball_x <= paddle_x + 2)
1284 		slope -= dir_x * 2;
1285 	      else if (ball_x <= paddle_x + 8)
1286 		slope -= dir_x;
1287 	      else if (ball_x <= paddle_x + 16)
1288 		slope += 0;
1289 	      else if (ball_x <= paddle_x + 22)
1290 		slope += dir_x;
1291 	      else if (ball_x <= paddle_x + 28)
1292 		slope += dir_x * 2;
1293 	      else
1294 		slope += dir_x * 3;
1295               if (slope < 0)
1296                 {
1297                   slope = -slope;
1298                   dir_x = -dir_x;
1299                 }
1300               if (slope > 6)
1301                 slope = 6;
1302 
1303 #ifndef USE_SDL
1304 	      playsound(1000, 10);
1305 #else
1306 	      playsound(snd_paddle);
1307 #endif
1308 
1309 	      add_score(1);
1310 	    }
1311 
1312 	  /* Fall off bottom of screen: */
1313 
1314 	  if (ball_y > 150)
1315 	    {
1316 	      ball_launched = 0;
1317 
1318 	      DrawImage(pix_launchon, window, 64, 225, 32, 13);
1319 
1320 #ifndef USE_SDL
1321 	      playsound(125, 100);
1322 #else
1323 	      playsound(snd_die);
1324 #endif
1325 
1326 	      balls--;
1327               draw_gamescreen();
1328 
1329 	      /* Game over!? */
1330 
1331 	      if (balls < 0)
1332 		{
1333 		  game_pending = 0;
1334 		  done = 1;
1335 
1336 		  for (i = 1000; i >= 100; i = i / 2)
1337 		    {
1338 #ifndef USE_SDL
1339 		      playsound(i, 200);
1340 #else
1341 		      playsound(snd_die);
1342 #endif
1343 		      usleep(200000);
1344 		    }
1345 		}
1346 	    }
1347 	}
1348 
1349       /* Go to next level? */
1350 
1351       if (num_bricks_left <= 0)
1352 	{
1353 	  /* Increment level: */
1354 
1355 	  level++;
1356 
1357 	  if (level > NUM_LEVELS)
1358 	    level = 1;
1359 
1360           ball_x  = 76;
1361           ball_bx = 4864;
1362           ball_y  = 116;
1363           ball_by = 7424;
1364           redraw_brick_x = -1;
1365           reset_level();
1366 	  draw_gamescreen();
1367           ball_launched = 0;
1368           DrawImage(pix_launchon, window, 64, 225, 32, 13);
1369 
1370 #ifdef USE_SDL
1371           if (use_sound)
1372           {
1373             Mix_HaltMusic();
1374           }
1375 #endif
1376 	}
1377 
1378       /* figure out if the ball is stuck in an infinite loop */
1379       if (counter > 1000)
1380         {
1381           if (slope > 4)
1382             slope--;
1383           else
1384             slope++;
1385         }
1386 
1387       /* Draw paddle: */
1388 
1389       DrawImage(pix_paddle, window, paddle_x, 150, 32, 10);
1390 
1391 
1392       /* Draw ball: */
1393 
1394       if (ball_launched)
1395         DrawImage(pix_ball, window, ball_x, ball_y, 8, 8);
1396 
1397 
1398 
1399       /* Draw special effects: */
1400 
1401 #ifdef USE_SDL
1402       for (y = 0; y < 6; y++)
1403       {
1404         for (x = 0; x < 10; x++)
1405 	{
1406 	  if (breaking_bricks[y][x].time > 0)
1407 	  {
1408             SDL_Surface * tmp_surf;
1409 	    char c;
1410 
1411 
1412 	    breaking_bricks[y][x].time--;
1413 
1414 	    dest.x = x * 16 + SDL_X_OFF;
1415 	    dest.y = y * 10 + SDL_Y_OFF;
1416 	    dest.w = 16;
1417 	    dest.h = 10;
1418 
1419 	    if (breaking_bricks[y][x].time % 2)
1420 	      c = breaking_bricks[y][x].kind;
1421 	    else
1422               c = cur_board[y][x];
1423 
1424             if (c == '-')
1425               tmp_surf = pix_brick1;
1426 	    else if (c == '=')
1427               tmp_surf = pix_brick2;
1428 	    else if (c == '#')
1429               tmp_surf = pix_brick3;
1430 	    else if (c == ':')
1431               tmp_surf = pix_brick4;
1432 	    else if (c == '/')
1433               tmp_surf = pix_brick5;
1434 	    else
1435 	      tmp_surf = NULL;
1436 
1437 	    if (tmp_surf != NULL)
1438 	      SDL_BlitSurface(tmp_surf, NULL, window, &dest);
1439 	    else
1440 	      SDL_FillRect(window, &dest, whitegc);
1441 	  }
1442 	}
1443       }
1444 #endif
1445 
1446 
1447       /* Delay... */
1448 
1449 #ifndef USE_SDL
1450       XSync(display, 0);
1451 #else
1452       SDL_Flip(window);
1453 #endif
1454 
1455       gettimeofday(&now, NULL);
1456 
1457       time_padding = 10000 - ((now.tv_sec - then.tv_sec) * 1000000 +
1458                               (now.tv_usec - then.tv_usec));
1459       if (time_padding > 0)
1460       {
1461         usleep(time_padding);
1462       }
1463 
1464 #ifdef USE_SDL
1465       if (use_sound && music)
1466       {
1467 	if (!Mix_PlayingMusic())
1468 	{
1469 	  Mix_PlayMusic(mus_game[((level - 1) / 2) % NUM_GAME_MUSICS], -1);
1470 	}
1471       }
1472 #endif
1473 
1474     }
1475   while (!done);
1476 
1477 
1478 #ifdef USE_SDL
1479   if (use_sound)
1480   {
1481     Mix_HaltMusic();
1482   }
1483 #endif
1484 }
1485 
1486 
1487 /* Draw game screen: */
1488 
1489 void draw_gamescreen(void)
1490 {
1491   char str[21];
1492   int x, y;
1493 
1494 
1495   /* Blank areas: */
1496 
1497 #ifndef USE_SDL
1498   XFillRectangle(display, window, whitegc, 0, 0, 160, 160);
1499   XFillRectangle(display, window, blackgc, 0, 160, 160, 80);
1500 #else
1501   SDL_Rect dest;
1502 
1503 
1504   dest.x = SDL_X_OFF;
1505   dest.y = SDL_Y_OFF;
1506   dest.w = 160;
1507   dest.h = 160;
1508   SDL_FillRect(window, &dest, SDL_MapRGB(window->format, 255, 255, 255));
1509 
1510   dest.x = SDL_X_OFF;
1511   dest.y = 160 + SDL_Y_OFF;
1512   dest.w = 160;
1513   dest.h = 80;
1514   SDL_FillRect(window, &dest, SDL_MapRGB(window->format, 0, 0, 0));
1515 #endif
1516 
1517 
1518   /* Draw water lapping: */
1519 
1520   for (x = 0; x <= 160; x = x + 29)
1521     {
1522       DrawImage(pix_water, window, x, 160, 29, 12);
1523     }
1524 
1525 
1526   /* (Status info) */
1527 
1528   DrawImage(pix_stats_toppipe, window, 0, 172, 160, 9);
1529   DrawImage(pix_stats_leftpipe, window, 0, 181, 11, 59);
1530   DrawImage(pix_stats_rightpipe, window, 149, 181, 11, 59);
1531   DrawImage(pix_stats, window, 12, 181, 52, 44);
1532 
1533 
1534   /* (Score) */
1535 
1536   sprintf(str, "%.6d", score);
1537   DrawNumbers(str, 70, 181);
1538 
1539 
1540   /* (Balls) */
1541 
1542   sprintf(str, "%.2d", balls);
1543   DrawNumbers(str, 70, 195);
1544 
1545 
1546   /* (Level) */
1547 
1548   sprintf(str, "%.2d", level);
1549   DrawNumbers(str, 70, 209);
1550 
1551 
1552   /* (Pause button) */
1553 
1554   DrawImage(pix_pause, window, 14, 225, 32, 13);
1555 
1556 
1557   /* (Launch button) */
1558 
1559   if (ball_launched == 0)
1560     DrawImage(pix_launchon, window, 64, 225, 32, 13);
1561   else
1562     DrawImage(pix_launchoff, window, 64, 225, 32, 13);
1563 
1564 
1565   /* (Sound control button) */
1566 
1567   if (sound == 1)
1568     DrawImage(pix_soundon, window, 126, 225, 18, 13);
1569   else
1570     DrawImage(pix_soundoff, window, 126, 225, 18, 13);
1571 
1572 
1573 #ifdef USE_SDL
1574   /* (Music control button) */
1575 
1576   if (music == 1)
1577     DrawImage(pix_musicon, window, 100, 225, 18, 13);
1578   else
1579     DrawImage(pix_musicoff, window, 100, 225, 18, 13);
1580 #endif
1581 
1582 
1583   /* (Bricks) */
1584 
1585   for (y = 0; y < 6; y++)
1586     {
1587       for (x = 0; x < 10; x++)
1588 	{
1589 	  draw_brick(x, y);
1590 	}
1591     }
1592 }
1593 
1594 
1595 /* Setup! */
1596 
1597 void setup(void)
1598 {
1599 #ifndef USE_SDL
1600   Window rootwindow;
1601   int screen, black, white;
1602   XSetWindowAttributes attr;
1603   XWMHints wmhints;
1604   unsigned long attr_mask;
1605   char wname[64];
1606 #else
1607   int i;
1608 #endif
1609   struct timeval tp;
1610 
1611 
1612   /* Seed random number generator: */
1613 
1614   gettimeofday(&tp, NULL);
1615   srand(tp.tv_sec + tp.tv_usec);
1616 
1617 
1618 #ifndef USE_SDL
1619   /* Connect to X server: */
1620 
1621   display = XOpenDisplay(NULL);
1622   if (display == NULL)
1623     {
1624       fprintf(stderr, "Can't connect to display!\n");
1625       exit(1);
1626     }
1627 
1628   screen = DefaultScreen(display);
1629   rootwindow = RootWindow(display, screen);
1630 
1631 
1632   /* Get primitive colors: */
1633 
1634   black = BlackPixel(display, screen);
1635   white = WhitePixel(display, screen);
1636 
1637 
1638   /* Open window: */
1639 
1640   attr.event_mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
1641 		     ButtonReleaseMask | PointerMotionMask |
1642 		     ExposureMask | VisibilityChangeMask);
1643 
1644   attr.border_pixel = black;
1645   attr.background_pixel = white;
1646   attr_mask = CWEventMask | CWBackPixel | CWBorderPixel;
1647 
1648   window = XCreateWindow(display, rootwindow, 0, 0, 160, 240, 0,
1649 			 DefaultDepthOfScreen(DefaultScreenOfDisplay(display)),
1650 			 InputOutput, DefaultVisual(display, screen),
1651 			 attr_mask, &attr);
1652 
1653 
1654   /* Set input hints and window name (so we appear in the window lists).
1655      Need to do this because of a bug in VRSM.  Not bad to do anyway, tho */
1656 
1657   wmhints.input = True;
1658   wmhints.flags |= InputHint;
1659   XSetWMHints(display, window, &wmhints);
1660 
1661   sprintf(wname, "BrickOut");
1662   XChangeProperty(display, window, XA_WM_NAME, XA_STRING, 8,
1663                   PropModeReplace, wname, strlen(wname));
1664 
1665 
1666   /* Create primitive graphics contexts: */
1667 
1668   whitegc = CreateGC(display, window, white, black);
1669   blackgc = CreateGC(display, window, black, black);
1670 
1671   pastegc = CreateGC(display, window, white, black);
1672 
1673 #else
1674 
1675   if (SDL_Init(SDL_INIT_VIDEO) < 0)
1676   {
1677     fprintf(stderr, "Error: Can't init SDL video\n%s\n", SDL_GetError());
1678     exit(1);
1679   }
1680 
1681   use_sound = 1;
1682 
1683   if (SDL_Init(SDL_INIT_AUDIO) < 0)
1684   {
1685     fprintf(stderr, "Warning: Can't init SDL audio\n%s\n", SDL_GetError());
1686     use_sound = 0;
1687   }
1688 
1689 
1690   window = SDL_SetVideoMode(240, 320, 16, SDL_HWSURFACE);
1691 
1692   if (window == NULL)
1693   {
1694     fprintf(stderr, "Error: Can't open window\n%s\n", SDL_GetError());
1695     exit(1);
1696   }
1697 
1698   whitegc = SDL_MapRGB(window->format, 255, 255, 255);
1699   blackgc = SDL_MapRGB(window->format, 0, 0, 0);
1700 
1701   SDL_WM_SetCaption("Brickout", "Brickout");
1702 #endif
1703 
1704 
1705   /* Create images: */
1706 
1707 #ifndef USE_SDL
1708   /* (Title screen stuff) */
1709 
1710   LoadImage(title_xpm, &pix_title);
1711   LoadImage(leftpipe_xpm, &pix_leftpipe);
1712 
1713 
1714   /* (Control buttons) */
1715 
1716   LoadImage(start_xpm, &pix_start);
1717   LoadImage(done_xpm, &pix_done);
1718   LoadImage(pause_xpm, &pix_pause);
1719   LoadImage(launchon_xpm, &pix_launchon);
1720   LoadImage(launchoff_xpm, &pix_launchoff);
1721   LoadImage(conton_xpm, &pix_conton);
1722   LoadImage(contoff_xpm, &pix_contoff);
1723   LoadImage(soundon_xpm, &pix_soundon);
1724   LoadImage(soundoff_xpm, &pix_soundoff);
1725   LoadImage(negon_xpm, &pix_negon);
1726   LoadImage(negoff_xpm, &pix_negoff);
1727   LoadImage(poson_xpm, &pix_poson);
1728   LoadImage(posoff_xpm, &pix_posoff);
1729 
1730 
1731   /* (Ball and paddle) */
1732 
1733   LoadImage(ball_xpm, &pix_ball);
1734   LoadImage(paddle_xpm, &pix_paddle);
1735 
1736 
1737   /* (Bricks) */
1738 
1739   LoadImage(brick1_xpm, &pix_brick1);
1740   LoadImage(brick2_xpm, &pix_brick2);
1741   LoadImage(brick3_xpm, &pix_brick3);
1742   LoadImage(brick4_xpm, &pix_brick4);
1743   LoadImage(brick5_xpm, &pix_brick5);
1744 
1745 
1746   /* (Stats stuff) */
1747 
1748   LoadImage(stats_xpm, &pix_stats);
1749   LoadImage(stats_toppipe_xpm, &pix_stats_toppipe);
1750   LoadImage(stats_leftpipe_xpm, &pix_stats_leftpipe);
1751   LoadImage(stats_rightpipe_xpm, &pix_stats_rightpipe);
1752   LoadImage(water_xpm, &pix_water);
1753   LoadImage(numbers_xpm, &pix_numbers);
1754 
1755 #else
1756 
1757   /* (Title screen stuff) */
1758 
1759   pix_title = LoadImage(title_xpm);
1760   pix_leftpipe = LoadImage(leftpipe_xpm);
1761 
1762 
1763   /* (Control buttons) */
1764 
1765   pix_start = LoadImage(start_xpm);
1766   pix_done = LoadImage(done_xpm);
1767   pix_pause = LoadImage(pause_xpm);
1768   pix_launchon = LoadImage(launchon_xpm);
1769   pix_launchoff = LoadImage(launchoff_xpm);
1770   pix_conton = LoadImage(conton_xpm);
1771   pix_contoff = LoadImage(contoff_xpm);
1772   pix_soundon = LoadImage(soundon_xpm);
1773   pix_soundoff = LoadImage(soundoff_xpm);
1774   pix_musicon = LoadImage(musicon_xpm);
1775   pix_musicoff = LoadImage(musicoff_xpm);
1776   pix_negon = LoadImage(negon_xpm);
1777   pix_negoff = LoadImage(negoff_xpm);
1778   pix_poson = LoadImage(poson_xpm);
1779   pix_posoff = LoadImage(posoff_xpm);
1780 
1781 
1782   /* (Ball and paddle) */
1783 
1784   pix_ball = LoadImage(ball_xpm);
1785   pix_paddle = LoadImage(paddle_xpm);
1786 
1787 
1788   /* (Bricks) */
1789 
1790   pix_brick1 = LoadImage(brick1_xpm);
1791   pix_brick2 = LoadImage(brick2_xpm);
1792   pix_brick3 = LoadImage(brick3_xpm);
1793   pix_brick4 = LoadImage(brick4_xpm);
1794   pix_brick5 = LoadImage(brick5_xpm);
1795 
1796 
1797   /* (Stats stuff) */
1798 
1799   pix_stats = LoadImage(stats_xpm);
1800   pix_stats_toppipe = LoadImage(stats_toppipe_xpm);
1801   pix_stats_leftpipe = LoadImage(stats_leftpipe_xpm);
1802   pix_stats_rightpipe = LoadImage(stats_rightpipe_xpm);
1803   pix_water = LoadImage(water_xpm);
1804   pix_numbers = LoadImage(numbers_xpm);
1805 
1806 #endif
1807 
1808 
1809 #ifdef USE_SDL
1810   if (Mix_OpenAudio(44100, AUDIO_S16, 2, 512) < 0)
1811   {
1812     fprintf(stderr, "Warning: Can't open audio at 44100 Hz / 16-bit\n%s\n",
1813 		    SDL_GetError());
1814 
1815     use_sound = 0;
1816   }
1817 
1818 
1819   /* Load sounds: */
1820 
1821   snd_wall = LoadSound(SOUNDDIR "wall.wav");
1822   snd_ceiling = LoadSound(SOUNDDIR "ceiling.wav");
1823   snd_metal = LoadSound(SOUNDDIR "metal.wav");
1824   snd_brick = LoadSound(SOUNDDIR "brick.wav");
1825   snd_unbreakable = LoadSound(SOUNDDIR "unbreakable.wav");
1826   snd_paddle = LoadSound(SOUNDDIR "paddle.wav");
1827   snd_die = LoadSound(SOUNDDIR "die.wav");
1828 
1829 
1830   /* Load music: */
1831 
1832   if (use_sound)
1833   {
1834     mus_title = Mix_LoadMUS(MUSICDIR "/Church.mod");
1835     if (mus_title == NULL)
1836     {
1837       fprintf(stderr, "Warning: Can't load music: " MUSICDIR "/Chruch.mod\n"
1838 		      "%s\n",
1839 		      SDL_GetError());
1840 
1841       use_sound = 0;
1842     }
1843   }
1844 
1845 
1846   for (i = 0; i < NUM_GAME_MUSICS && use_sound; i++)
1847   {
1848     mus_game[i] = Mix_LoadMUS(music_fnames[i]);
1849     if (mus_game[i] == NULL)
1850     {
1851       fprintf(stderr, "Warning: Can't load music: %s\n%s\n",
1852 		      music_fnames[i], SDL_GetError());
1853       use_sound = 0;
1854     }
1855   }
1856 
1857 #endif
1858 
1859 
1860   /* Bring window up! */
1861 
1862 #ifndef USE_SDL
1863   XMapWindow(display, window);
1864   XMapRaised(display, window);
1865   XSync(display, 0);
1866 #else
1867   SDL_Flip(window);
1868 #endif
1869 }
1870 
1871 
1872 /* Load settings from file: */
1873 
1874 void loadsettings(void)
1875 {
1876   sound = 1;
1877 #ifdef USE_SDL
1878   music = 1;
1879 #endif
1880   game_pending = 0;
1881 }
1882 
1883 
1884 
1885 /* Create a graphics context: */
1886 
1887 #ifndef USE_SDL
1888 GC CreateGC(Display *display, Drawable drawable, unsigned long forecolor,
1889             unsigned long backcolor)
1890 {
1891   XGCValues xgcvalues;
1892   GC gc;
1893 
1894   xgcvalues.foreground = forecolor;
1895   xgcvalues.background = backcolor;
1896   gc = XCreateGC(display,drawable,(GCForeground | GCBackground),
1897                  &xgcvalues);
1898 
1899   return(gc);
1900 }
1901 #endif
1902 
1903 
1904 /* Convert an image from XPM data to a pixmap: */
1905 
1906 #ifndef USE_SDL
1907 void LoadImage(char ** xpm, Pixmap * pix)
1908 {
1909   XpmCreatePixmapFromData(display, window, xpm, pix, NULL, NULL);
1910 }
1911 #else
1912 Pixmap LoadImage(const char * file)
1913 {
1914   Pixmap pix;
1915   pix = SDL_LoadBMP(file);
1916 
1917   if (pix == NULL)
1918   {
1919     fprintf(stderr, "Error: Can't load %s\n%s\n", file, SDL_GetError());
1920     exit(1);
1921   }
1922 
1923   return pix;
1924 }
1925 #endif
1926 
1927 
1928 /* Paste a pixmap onto the screen: */
1929 
1930 void DrawImage(Pixmap pix, Drawable dest, int x, int y, int w, int h)
1931 {
1932 #ifndef USE_SDL
1933   XSetClipOrigin(display, pastegc, x, y);
1934 
1935   XCopyArea(display, pix, dest, pastegc, 0, 0, w, h, x, y);
1936 #else
1937   SDL_Rect dest_rect;
1938 
1939   dest_rect.x = x + SDL_X_OFF;
1940   dest_rect.y = y + SDL_Y_OFF;
1941   dest_rect.w = w;
1942   dest_rect.h = h;
1943 
1944   SDL_BlitSurface(pix, NULL, dest, &dest_rect);
1945 #endif
1946 }
1947 
1948 
1949 /* Draw numbers: */
1950 
1951 void DrawNumbers(char * str, int x, int y)
1952 {
1953   int i;
1954 #ifdef USE_SDL
1955   SDL_Rect src, dest;
1956 #endif
1957 
1958   for (i = 0; i < strlen(str); i++)
1959     {
1960 #ifndef USE_SDL
1961       XSetClipOrigin(display, pastegc, x, y);
1962 #else
1963       src.x = (str[i] - '0') * 8;
1964       src.y = 0;
1965       src.w = 8;
1966       src.h = 14;
1967 
1968       dest.x = x + SDL_X_OFF;
1969       dest.y = y + SDL_Y_OFF;
1970 #endif
1971 
1972       if (str[i] >= '0' && str[i] <= '9')
1973 	{
1974 #ifndef USE_SDL
1975 	  XCopyArea(display, pix_numbers, window, pastegc,
1976 		    (str[i] - '0') * 8, 0, 8, 14, x, y);
1977 #else
1978 	  SDL_BlitSurface(pix_numbers, &src, window, &dest);
1979 #endif
1980 	}
1981 
1982       x = x + 8;
1983     }
1984 }
1985 
1986 
1987 
1988 /* Play a sound! */
1989 
1990 #ifndef USE_SDL
1991 
1992 void playsound(int pitch, int duration)
1993 {
1994   XKeyboardControl vals;
1995 
1996   if (sound)
1997     {
1998       vals.bell_pitch = pitch;
1999       vals.bell_duration = duration;
2000       vals.bell_percent = 100;
2001 
2002       XChangeKeyboardControl(display,
2003 			     (KBBellPitch | KBBellDuration | KBBellPercent),
2004 			     &vals);
2005       XBell(display, 100);
2006       XFlush(display);
2007     }
2008 }
2009 
2010 #else
2011 
2012 void playsound(Mix_Chunk * snd)
2013 {
2014   if (use_sound && sound)
2015   {
2016     Mix_PlayChannel(-1, snd, 0);
2017   }
2018 }
2019 
2020 #endif
2021 
2022 /* Launch a ball! */
2023 
2024 void launch_ball(void)
2025 {
2026   int ball_x;
2027   /* Launch the ball with a random direction/etc: */
2028 
2029   ball_launched = 1;
2030   ball_x = (rand() % 60) - 30 + paddle_x;
2031   if (ball_x <= 10)
2032     ball_x = 10;
2033   if (ball_x >= 150)
2034     ball_x = 150;
2035   ball_bx = ball_x << 6;
2036   ball_by = 65 << 6;
2037   slope = (rand() % 5) + 1;
2038   brake = 8;
2039   dir_x = ((rand() % 2) * 2) - 1;
2040   dir_y = 1;
2041 
2042   /* Disable Launch button: */
2043 
2044   DrawImage(pix_launchoff, window, 64, 225, 32, 13);
2045 }
2046 
2047 
2048 /* Increment score, and redraw it on the screen: */
2049 
2050 void add_score(int val)
2051 {
2052   char str[20];
2053 
2054   score = score + val;
2055 
2056   sprintf(str, "%.6d", score);
2057   DrawNumbers(str, 70, 181);
2058 }
2059 
2060 
2061 /* Draw a brick: */
2062 
2063 void draw_brick(int x, int y)
2064 {
2065   char c;
2066 #ifdef USE_SDL
2067   SDL_Rect dest;
2068 #endif
2069 
2070   c = cur_board[y][x];
2071 
2072   if (c == '-')
2073     DrawImage(pix_brick1, window, x * 16, y * 10, 16, 10);
2074   else if (c == '=')
2075     DrawImage(pix_brick2, window, x * 16, y * 10, 16, 10);
2076   else if (c == '#')
2077     DrawImage(pix_brick3, window, x * 16, y * 10, 16, 10);
2078   else if (c == ':')
2079     DrawImage(pix_brick4, window, x * 16, y * 10, 16, 10);
2080   else if (c == '/')
2081     DrawImage(pix_brick5, window, x * 16, y * 10, 16, 10);
2082   else
2083    {
2084 #ifndef USE_SDL
2085      XFillRectangle(display, window, whitegc, x * 16, y * 10, 16, 10);
2086 #else
2087      dest.x = x * 16 + SDL_X_OFF;
2088      dest.y = y * 10 + SDL_Y_OFF;
2089      dest.w = 16;
2090      dest.h = 10;
2091 
2092      SDL_FillRect(window, &dest, whitegc);
2093 #endif
2094     }
2095 }
2096 
2097 
2098 /* Initialize board: */
2099 
2100 void reset_level(void)
2101 {
2102   int x, y;
2103 
2104   num_bricks_left = 0;
2105 
2106   for (y = 0; y < 6; y++)
2107     {
2108       for (x = 0; x < 10; x++)
2109 	{
2110 #ifdef USE_SDL
2111             breaking_bricks[y][x].time = (rand() % 4) + 8;
2112             breaking_bricks[y][x].kind = cur_board[y][x];
2113 #endif
2114 
2115 	  cur_board[y][x] = boards[level - 1][y][x];
2116 
2117 	  if (cur_board[y][x] != ' ' &&
2118 	      cur_board[y][x] != ':')
2119 	    {
2120 	      num_bricks_left++;
2121 	    }
2122 
2123 	}
2124     }
2125 }
2126 
2127 /* Check to see if we hit the given brick */
2128 
2129 int hit_brick(int x,int y)
2130 {
2131   int dx, dy;
2132   int corner, forward_ball, forward_corner;
2133 
2134   /* don't check anything if there is no brick here */
2135   if (cur_board[y][x] == ' ')
2136     return 0;
2137 
2138   dx = ((ball_bx >> 6) + 4) - (x << 4);
2139   dy = ((ball_by >> 6) + 4) - (y * 10);
2140 
2141   /* skip out if the ball is outside the sides */
2142   /* of the brick                              */
2143   if (dx < -4 || dx > 19)
2144     return 0;
2145   if (dy < -4 || dy > 13)
2146     return 0;
2147 
2148   /* check for clean hit on top or bottom edge of brick */
2149   if (dx >= -1 && dx <= 16)
2150     {
2151       dir_y = -dir_y;
2152       if (dy < 5)
2153           ball_by -= ((dy + 4) << 6);
2154       else
2155           ball_by += ((14 - dy) << 6);
2156       return 1;
2157     }
2158 
2159   /* check for vertical slope - must be a corner hit */
2160   if (slope == 0)
2161     {
2162       if (dx < -2)
2163         slope = 2;
2164       else
2165         slope = 1;
2166       dir_x = (dy >= 5) ? 1 : -1;
2167       dir_y = -dir_y;
2168       if (dy < 5)
2169           ball_by -= ((dy + 4) << 6);
2170       else
2171           ball_by += ((14 - dy) << 6);
2172       return 1;
2173     }
2174 
2175   /* check for clean hit on side edge of brick */
2176   if (dy >= -1 && dy > 10)
2177     {
2178       dir_x = -dir_x;
2179       if (dx < 8)
2180           ball_bx -= ((dx + 4) << 6);
2181       else
2182           ball_bx += ((20 - dx) << 6);
2183       return 1;
2184     }
2185 
2186   /* if the slope is close to 45 (#4) then  */
2187   /* use a 6x6 square to approximate the    */
2188   /* ball at the corners.                   */
2189   if (slope >= 3 && slope <= 5)
2190     {
2191       dx -= 1;
2192       dy -= 1;
2193       if (dx < -3 || dx > 18 ||
2194           dy < -3 || dy > 12)
2195         {
2196           /* mark this brick for redrawing as */
2197           /* the ball will probaby erase part */
2198           /* of it as it goes by (because the */
2199           /* ball is really a square pixmap   */
2200           /* that just displays a round ball  */
2201           redraw_brick_x = x;
2202           redraw_brick_y = y;
2203           return 0;
2204         }
2205       dx += 1;
2206       dy += 1;
2207     }
2208 
2209   /* the ball hit a corner so figure that out now   */
2210 
2211   /* figure out which corner the ball hit           */
2212   /* 0        1                                     */
2213   /*  +------+                                      */
2214   /*  |      |                                      */
2215   /*  +------+                                      */
2216   /* 2        3                                     */
2217   corner = 0;
2218   if (dx >= 8)
2219     corner |= 1;
2220   if (dy >= 5)
2221     corner |= 2;
2222   forward_corner = (corner == 1 || corner == 2) ? 1 : 0;
2223   forward_ball   = (dir_y != dir_x) ? 1 : 0;
2224 
2225   /* first figure out if we hit the corner directly */
2226   /* or with a glancing blow                        */
2227   if (forward_corner ^ forward_ball)
2228     {
2229       /* for glancing blows, only toggle the dir_x   */
2230       /* or dir_y that makes sense for the corner    */
2231       if ((dir_x == 1 && dx < 8) ||
2232           (dir_x == -1 && dx >= 8))
2233         dir_x = -dir_x;
2234       else
2235         dir_y = -dir_y;
2236     }
2237   else
2238     {
2239       /* it was a direct hit on the corner */
2240       dir_x = -dir_x;
2241       dir_y = -dir_y;
2242     }
2243 
2244   /* And adjust the ball so that it is not   */
2245   /* overlapping the brick anymore           */
2246   if ((dir_x == 1 && dx < 8) ||
2247       (dir_x == -1 && dx >= 8))
2248     {
2249       if (dy < 5)
2250         ball_by -= ((dy + 4) << 6);
2251       else
2252         ball_by += ((14 - dy) << 6);
2253     }
2254   else
2255     {
2256       if (dx < 8)
2257         ball_bx -= ((dx + 4) << 6);
2258       else
2259         ball_bx += ((20 - dx) << 6);
2260     }
2261 
2262   return 1;
2263 }
2264 
2265 
2266 #ifdef USE_SDL
2267 
2268 Mix_Chunk * LoadSound(char * fname)
2269 {
2270   Mix_Chunk * s;
2271 
2272 
2273   if (use_sound)
2274   {
2275     s = Mix_LoadWAV(fname);
2276 
2277     if (s == NULL)
2278     {
2279       use_sound = 0;
2280 
2281       fprintf(stderr, "Warning: Can't load sound: %s\n%s\n", fname,
2282 	      SDL_GetError());
2283     }
2284 
2285     return s;
2286   }
2287   else
2288     return NULL;
2289 }
2290 
2291 #endif
2292