1 
2 /*
3 
4   A linux clone of a windows game with the same name.
5   Written from scratch in 4 days.
6 
7   License: GPLv2
8 
9   paxed@alt.org
10 
11   20080502
12 
13 
14 
15   TODO:
16   -when hovering mouse over a block in the map, show the targeting bar
17    towards all four directions.
18   -max. # of moves per level?
19   -highscore entries?
20   -smooth block movement?
21   -configurable keys
22   -help screens
23   -level editor?
24 
25  */
26 
27 
28 #include "SDL/SDL.h"
29 #include "SDL_image.h"
30 #include "SDL_mixer.h"
31 
32 #include <time.h>
33 #include <stdarg.h>
34 #include <string.h>
35 
36 #define ARRAY_SIZE(x) (int)(sizeof(x) / sizeof(x[0]))
37 
38 #define TRUE  1
39 #define FALSE 0
40 
41 #define boolean int
42 
43 
44 #define GAME_NAME "brickshooter"
45 #define GAME_VERSION "v0.04"
46 
47 #define GKEY_QUIT                 SDLK_ESCAPE
48 #define GKEY_LEFT SDLK_LEFT
49 #define GKEY_RIGHT SDLK_RIGHT
50 #define GKEY_UP SDLK_UP
51 #define GKEY_DOWN SDLK_DOWN
52 #define GKEY_FIRE SDLK_SPACE
53 #define GKEY_TURN_CW 'x'
54 #define GKEY_TURN_CCW 'z'
55 
56 SDL_Surface *screen = NULL;
57 
58 Uint32 button_color;
59 Uint32 button_hilite_color;
60 Uint32 window_color;
61 
62 Uint32 cursor_color;
63 Uint32 allow_color;
64 Uint32 bg_color;
65 
66 
67 boolean is_fullscreen = FALSE;
68 boolean is_paused = FALSE;
69 boolean debugging_level = 0;
70 boolean do_quit = FALSE;
71 int SCREEN_WID = 800;
72 int SCREEN_HEI = 600;
73 int SCREEN_BPP = 24;
74 
75 enum {
76     DIR_N = 0,
77     DIR_E,
78     DIR_S,
79     DIR_W,
80     DIR_NONE
81 };
82 
83 
84 #define MAP_WID 10
85 #define MAP_HEI 10
86 #define MAP_BORDER_WID 3
87 
88 
89 int TILE_WID = 32;
90 int TILE_HEI = 32;
91 int TILE_SPACE = 2;
92 
93 int SCORE_X = 64+16;
94 int SCORE_Y = 32+16;
95 
96 int SCORE_BG_X = 32;
97 int SCORE_BG_Y = 32;
98 
99 
100 int LEVEL_POS_X = 800 - 100;
101 int LEVEL_POS_Y = 32+16;
102 
103 int LEVELTXT_BG_X = 800 - 196;
104 int LEVELTXT_BG_Y = 32;
105 
106 
107 #define MAX_FLOOD_HEAP 128
108 
109 
110 int anim_delay = 1;
111 int block_move_speed = 2; /* 0, 1, 2 */
112 
113 
114 boolean audio_use = TRUE;
115 int audio_rate = 44100;
116 Uint16 audio_format = AUDIO_S16SYS;
117 int audio_num_channels = 1;
118 int audio_buffers = 4096;
119 
120 enum soundtypes {
121     SND_EXPLODE = 0,
122     SND_GAMEOVER,
123     SND_BLOCKHIT,
124     SND_NEXTLEVEL,
125     SND_BLOCKOUT,
126     SND_FINISHSET,
127     SND_CANTMOVE,
128     SND_SHOOTBLOCK,
129     SND_STARTUP,
130     NUM_SOUNDS
131 };
132 
133 Mix_Chunk *audio_sound[NUM_SOUNDS];
134 
135 int audio_channels[NUM_SOUNDS];
136 
137 char *audio_sound_files[NUM_SOUNDS] = {
138     "/usr/local/share/brickshooter/snd/explode.wav",
139     "/usr/local/share/brickshooter/snd/gameover.wav",
140     "/usr/local/share/brickshooter/snd/blockhit.wav",
141     "/usr/local/share/brickshooter/snd/nextlevel.wav",
142     "/usr/local/share/brickshooter/snd/blockout.wav",
143     "/usr/local/share/brickshooter/snd/finishset.wav",
144     "/usr/local/share/brickshooter/snd/cantmove.wav",
145     "/usr/local/share/brickshooter/snd/shootblock.wav",
146     "/usr/local/share/brickshooter/snd/startup.wav"
147 };
148 
149 
150 const char *charset_chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,;:'|-!\"#$%&/()=?+\\*_<>@ ";
151 
152 SDL_Surface *charset_img = NULL;
153 int charset_charwid = 16;
154 
155 SDL_Surface *background = NULL;
156 SDL_Surface *tilegfx = NULL;
157 SDL_Surface *cursor_img = NULL;
158 SDL_Surface *target_pointer_img = NULL;
159 SDL_Surface *score_bg_img = NULL;
160 SDL_Surface *leveltxt_bg_img = NULL;
161 SDL_Surface *window_border = NULL;
162 
163 struct mapcell {
164     int block;
165     int dir;
166 
167     int xofs;
168     int yofs;
169 
170     int xadj;
171     int yadj;
172 
173     int steps;
174 };
175 
176 
177 struct mapcell map[MAP_WID][MAP_HEI];
178 struct mapcell border_n[MAP_BORDER_WID][MAP_WID];
179 struct mapcell border_s[MAP_BORDER_WID][MAP_WID];
180 struct mapcell border_w[MAP_BORDER_WID][MAP_HEI];
181 struct mapcell border_e[MAP_BORDER_WID][MAP_HEI];
182 
183 int cursor_pos = MAP_WID / 2;
184 int cursor_side = DIR_N;
185 int cursor_shown = TRUE;
186 
187 int used_tiletypes[20];
188 int used_tiletypes_count = 0;
189 
190 int score = 0;
191 int level = 1;
192 int moves = 0;
193 
194 long level_file_len = -1;
195 char *level_file = NULL;
196 
197 char *def_levelset = "aigyptos";
198 
199 char levelset_fname[64];
200 char levelset_name[64];
201 
202 int random_levelset = -1;
203 
204 
205 int program_state = 0; /* 0 = main menu, 1 = playing */
206 
207 
208 
209 int EXPLOSION_FRAMES = -1;
210 struct explosion_anim {
211     int x,y;
212     int state;
213 };
214 SDL_Surface *explosion_img = NULL;
215 
216 #define MAX_EXPLOSIONS 32
217 struct explosion_anim explosions[MAX_EXPLOSIONS];
218 
219 
220 void
play_sound(int snd)221 play_sound(int snd)
222 {
223     if (audio_use)
224 	audio_channels[snd] = Mix_PlayChannel(-1, audio_sound[snd], 0);
225 }
226 
227 
228 void
explosions_init()229 explosions_init()
230 {
231     int i;
232     for (i = 0; i < MAX_EXPLOSIONS; i++)
233 	explosions[i].x = explosions[i].y = explosions[i].state = -1;
234 
235     if (explosion_img)
236 	EXPLOSION_FRAMES = explosion_img->h / explosion_img->w;
237     else EXPLOSION_FRAMES = -1;
238 }
239 
240 int
explosions_active()241 explosions_active()
242 {
243     int i, count = 0;
244     for (i = 0; i < MAX_EXPLOSIONS; i++)
245 	if (explosions[i].state != -1)
246 	    count++;
247     return count;
248 }
249 
250 void
explosions_add(int x,int y)251 explosions_add(int x, int y)
252 {
253     int i;
254     for (i = 0; i < MAX_EXPLOSIONS; i++)
255 	if (explosions[i].state == -1) {
256 	    explosions[i].x = x;
257 	    explosions[i].y = y;
258 	    explosions[i].state = EXPLOSION_FRAMES-1;
259 	    return;
260 	}
261 }
262 
263 void
explosions_draw(SDL_Surface * surf)264 explosions_draw(SDL_Surface *surf)
265 {
266     int i;
267     SDL_Rect src, dst;
268 
269     src.w = explosion_img->w;
270     src.h = src.w;
271 
272     src.x = 0;
273 
274     for (i = 0; i < MAX_EXPLOSIONS; i++)
275 	if (explosions[i].state >= 0) {
276 	    dst.x = explosions[i].x;
277 	    dst.y = explosions[i].y;
278 	    src.y = src.h*explosions[i].state;
279 	    SDL_BlitSurface(explosion_img, &src, surf, &dst);
280 	}
281 }
282 
283 void
explosions_anim()284 explosions_anim()
285 {
286     int i;
287     for (i = 0; i < MAX_EXPLOSIONS; i++)
288 	if (explosions[i].state >= 0) explosions[i].state--;
289 }
290 
291 
292 SDL_Surface *
copysurface(SDL_Surface * surf)293 copysurface(SDL_Surface* surf)
294 {
295     return SDL_ConvertSurface(surf, surf->format, surf->flags);
296 }
297 
298 /*
299 void
300 screen_fadeout()
301 {
302     int blockwid = 10;
303     int blockhei = 10;
304 
305     int xsize = SCREEN_WID / blockwid;
306     int ysize = SCREEN_HEI / blockhei;
307     int x,y;
308 
309     Uint32 fadeout_color = SDL_MapRGB(screen->format, 0, 0, 0);
310 
311     SDL_Rect r;
312 
313     r.w = xsize;
314     r.h = ysize;
315 
316     for (y = 0; y < (blockhei+1)/2; y++) {
317 	for (x = 0; x < blockwid; x++) {
318 	    r.y = y*ysize;
319 	    r.x = x*xsize;
320 	    r.w = xsize;
321 	    r.h = ysize;
322 	    SDL_FillRect(screen, &r, fadeout_color);
323 
324 	    r.y = (blockhei-y-1)*ysize;
325 	    r.x = (blockwid-x-1)*xsize;
326 	    r.w = xsize;
327 	    r.h = ysize;
328 	    SDL_FillRect(screen, &r, fadeout_color);
329 
330 	    SDL_Flip(screen);
331 	}
332     }
333 }
334 */
335 
336 void
write_string(SDL_Surface * surf,int x,int y,char * str)337 write_string(SDL_Surface *surf, int x, int y, char *str)
338 {
339     int i;
340 
341     SDL_Rect src, dst;
342 
343     charset_charwid = charset_img->w / strlen(charset_chars);
344 
345     if (x < 0)
346 	x = (SCREEN_WID/2) - ((strlen(str)*charset_charwid)/2);
347 
348     for (i = 0; i < strlen(str); i++) {
349 	src.x = (strchr(charset_chars, str[i]) - charset_chars)*charset_charwid;
350 	src.y = 0;
351 	src.h = charset_img->h;
352 	src.w = charset_charwid;
353 
354 	dst.x = x + i*charset_charwid;
355 	dst.y = y;
356 
357 	SDL_BlitSurface(charset_img, &src, surf, &dst);
358 
359     }
360 }
361 
362 void
draw_window(SDL_Surface * surf,int x,int y,int w,int h)363 draw_window(SDL_Surface *surf, int x, int y, int w, int h)
364 {
365     SDL_Rect r, src;
366 
367     int bw, bh, i, dx,dy;
368 
369     if ((w % charset_charwid) > 0)
370 	w += (charset_charwid - (w % charset_charwid));
371     if ((h % charset_img->h) > 0)
372 	h += (charset_img->h - (h % charset_img->h));
373 
374     if (!window_border) {
375 	r.x = x;
376 	r.y = y;
377 	r.w = w;
378 	r.h = h;
379 	SDL_FillRect(surf, &r, window_color);
380 	return;
381     }
382 
383     bw = (window_border->w - charset_charwid) / 2;
384     bh = (window_border->h - charset_img->h) / 2;
385 
386 
387     for (i = 0; i < w; i += charset_charwid) {
388 	src.x = bw;
389 	src.y = 0;
390 	src.w = charset_charwid;
391 	src.h = bh;
392 	r.x = x + i;
393 	r.y = y - bh;
394 	SDL_BlitSurface(window_border, &src, surf, &r);
395 
396 	src.y = window_border->h - bh;
397 	r.y = y + h;
398 	SDL_BlitSurface(window_border, &src, surf, &r);
399 
400     }
401 
402     for (i = 0; i < h; i += charset_img->h) {
403 	src.x = 0;
404 	src.y = bh;
405 	src.w = bw;
406 	src.h = charset_img->h;
407 	r.x = x - bw;
408 	r.y = y + i;
409 	SDL_BlitSurface(window_border, &src, surf, &r);
410 
411 	src.x = window_border->w - bw;
412 	r.x = x + w;
413 	SDL_BlitSurface(window_border, &src, surf, &r);
414     }
415 
416     src.x = bw;
417     src.y = bh;
418     src.w = charset_charwid;
419     src.h = charset_img->h;
420     for (dx = 0; dx < w; dx += charset_charwid) {
421 	for (dy = 0; dy < h; dy += charset_img->h) {
422 	    r.x = x + dx;
423 	    r.y = y + dy;
424 	    SDL_BlitSurface(window_border, &src, surf, &r);
425 	}
426     }
427 
428     /* top left */
429     src.x = 0;
430     src.y = 0;
431     src.w = bw;
432     src.h = bh;
433     r.x = x - bw;
434     r.y = y - bh;
435     SDL_BlitSurface(window_border, &src, surf, &r);
436 
437     /* top right */
438     src.x = window_border->w - bw;
439     src.y = 0;
440     r.x = x + w;
441     SDL_BlitSurface(window_border, &src, surf, &r);
442 
443     /* bottom left */
444     src.x = 0;
445     src.y = window_border->h - bh;
446     r.x = x - bw;
447     r.y = y + h;
448     SDL_BlitSurface(window_border, &src, surf, &r);
449 
450     /* bottom right */
451     src.x = window_border->w - bw;
452     src.y = window_border->h - bh;
453     r.x = x + w;
454     r.y = y + h;
455     SDL_BlitSurface(window_border, &src, surf, &r);
456 }
457 
458 
459 /*
460 int
461 get_yn(SDL_Surface *screen, char *msg, int defval)
462 {
463     int ok = 0;
464 
465     SDL_Rect dst, yesbtn, nobtn;
466 
467     int box_border = charset_charwid / 2;
468 
469     SDL_Event event;
470 
471     int retval = defval;
472 
473     int hilight = 0;
474 
475     char yes_str[] = "Yes ";
476     char no_str[]  = " No ";
477 
478     do {
479 
480 	if (strlen(msg) < 9)
481 	    dst.w = (9*charset_charwid) + box_border*2;
482 	else
483 	    dst.w = (strlen(msg)*charset_charwid) + box_border*2;
484 
485 	dst.h = (charset_img->h * 3) + box_border*2;
486 
487 	dst.x = (SCREEN_WID/2) - (dst.w/2);
488 	dst.y = (SCREEN_HEI/2) - (dst.h/2);
489 
490 	draw_window(screen, dst.x, dst.y, dst.w, dst.h);
491 
492 	write_string(screen, -1, dst.y+box_border, msg);
493 
494 	yesbtn.x = dst.x + box_border;
495 	yesbtn.y = dst.y + charset_img->h*2+box_border;
496 	yesbtn.w = strlen(yes_str)*charset_charwid;
497 	yesbtn.h = charset_img->h;
498 	SDL_FillRect(screen, &yesbtn, (hilight & 1) ? button_hilite_color : button_color);
499 
500 	nobtn.x = dst.x+box_border+5*charset_charwid;
501 	nobtn.w = strlen(no_str)*charset_charwid;
502 	nobtn.y = yesbtn.y;
503 	nobtn.h = yesbtn.h;
504 	SDL_FillRect(screen, &nobtn, (hilight & 2) ? button_hilite_color : button_color);
505 
506 	write_string(screen, dst.x+box_border, dst.y+charset_img->h*2+box_border, yes_str);
507 	write_string(screen, dst.x+box_border+5*charset_charwid, dst.y+charset_img->h*2+box_border, no_str);
508 
509 	SDL_Flip(screen);
510 
511 	SDL_Delay(25);
512 
513 	while (SDL_PollEvent(&event)) {
514 	    switch(event.type) {
515 	    case SDL_QUIT:
516 		ok = 1;
517 		break;
518 	    case SDL_KEYDOWN:
519 		switch (event.key.keysym.sym) {
520 		default: break;
521 		case 'y':
522 		case 'Y':
523 		    retval = TRUE;
524 		    ok = 1;
525 		    break;
526 		case 'n':
527 		case 'N':
528 		    retval = FALSE;
529 		    ok = 1;
530 		    break;
531 		case SDLK_ESCAPE:
532 		    ok = 1;
533 		    break;
534 		case SDLK_LEFT:
535 		    hilight = 1;
536 		    break;
537 		case SDLK_RIGHT:
538 		    hilight = 2;
539 		    break;
540 		case SDLK_RETURN:
541 		case SDLK_SPACE:
542 		case SDLK_KP_ENTER:
543 		    if (hilight == 1 || hilight == 2) {
544 			retval = (hilight == 1) ? TRUE : FALSE;
545 			ok = 1;
546 		    }
547 		    break;
548 		}
549 		break;
550 	    case SDL_MOUSEMOTION:
551 		hilight = 0;
552 		if (event.motion.x >= yesbtn.x && event.motion.y >= yesbtn.y &&
553 		    event.motion.x <= yesbtn.x+yesbtn.w && event.motion.y <= yesbtn.y+yesbtn.h)
554 		    hilight = 1;
555 		if (event.motion.x >= nobtn.x && event.motion.y >= nobtn.y &&
556 		    event.motion.x <= nobtn.x+nobtn.w && event.motion.y <= nobtn.y+nobtn.h)
557 		    hilight = 2;
558 		break;
559 	    case SDL_MOUSEBUTTONUP:
560 	    case SDL_MOUSEBUTTONDOWN:
561 		hilight = 0;
562 		if (event.button.x >= yesbtn.x && event.button.y >= yesbtn.y &&
563 		    event.button.x <= yesbtn.x+yesbtn.w && event.button.y <= yesbtn.y+yesbtn.h) {
564 		    hilight = 1;
565 		    retval = TRUE;
566 		    if (event.type == SDL_MOUSEBUTTONUP) ok = 1;
567 		}
568 		if (event.button.x >= nobtn.x && event.button.y >= nobtn.y &&
569 		    event.button.x <= nobtn.x+nobtn.w && event.button.y <= nobtn.y+nobtn.h) {
570 		    hilight = 2;
571 		    retval = FALSE;
572 		    if (event.type == SDL_MOUSEBUTTONUP) ok = 1;
573 		}
574 		break;
575 	    }
576 	}
577 
578     } while (!ok);
579 
580     return retval;
581 }
582 */
583 
584 void
show_popup(SDL_Surface * screen,char * msg,long timeout)585 show_popup(SDL_Surface *screen, char *msg, long timeout)
586 {
587     int ok = 0;
588 
589     SDL_Rect dst, btn;
590 
591     int box_border = charset_charwid / 2;
592 
593     SDL_Event event;
594 
595     int hilight = 0;
596     int got_input = 0;
597 
598     char ok_str[] = " OK ";
599 
600     time_t secs = time(NULL);
601 
602     SDL_Surface *bg_surf = copysurface(screen);
603 
604     do {
605 
606 	if (strlen(msg) < 4)
607 	    dst.w = (4*charset_charwid) + box_border*2;
608 	else
609 	    dst.w = (strlen(msg)*charset_charwid) + box_border*2;
610 
611 	dst.h = (charset_img->h * 3) + box_border*2;
612 
613 	dst.x = (SCREEN_WID/2) - (dst.w/2);
614 	dst.y = (SCREEN_HEI/2) - (dst.h/2);
615 
616 	if (bg_surf)
617 	    SDL_BlitSurface(bg_surf, NULL, screen, NULL);
618 
619 	draw_window(screen, dst.x, dst.y, dst.w, dst.h);
620 
621 	write_string(screen, dst.x+box_border, dst.y+box_border, msg);
622 
623 
624 	btn.x = (SCREEN_WID/2) - ((strlen(ok_str)*charset_charwid)/2);
625 	btn.y = dst.y + charset_img->h*2+box_border;
626 	btn.w = strlen(ok_str)*charset_charwid;
627 	btn.h = charset_img->h;
628 	SDL_FillRect(screen, &btn, (hilight & 1) ? button_hilite_color : button_color);
629 
630 	write_string(screen, -1, dst.y+charset_img->h*2+box_border, ok_str);
631 
632 	SDL_Flip(screen);
633 
634 	SDL_Delay(25);
635 
636 	if (timeout > 0) {
637 	    if (time(NULL) >= secs + timeout) ok = 1;
638 	}
639 
640 
641 	while (SDL_PollEvent(&event)) {
642 	    switch(event.type) {
643 	    case SDL_QUIT:
644 		ok = 1;
645 		break;
646 	    case SDL_KEYDOWN:
647 	    case SDL_KEYUP:
648 		if (event.key.keysym.sym == SDLK_SPACE ||
649 		    event.key.keysym.sym == SDLK_KP_ENTER ||
650 		    event.key.keysym.sym == SDLK_RETURN ||
651 		    event.key.keysym.sym == SDLK_ESCAPE) {
652 		    if (event.type == SDL_KEYUP && got_input) ok = 1;
653 		    else got_input = 1;
654 		} else got_input = 0;
655 		break;
656 	    case SDL_MOUSEMOTION:
657 		hilight = 0;
658 		if (event.motion.x >= btn.x && event.motion.y >= btn.y &&
659 		    event.motion.x <= btn.x+btn.w && event.motion.y <= btn.y+btn.h)
660 		    hilight = 1;
661 		break;
662 	    case SDL_MOUSEBUTTONUP:
663 	    case SDL_MOUSEBUTTONDOWN:
664 		hilight = 0;
665 		if (event.motion.x >= btn.x && event.motion.y >= btn.y &&
666 		    event.motion.x <= btn.x+btn.w && event.motion.y <= btn.y+btn.h) {
667 		    hilight = 1;
668 		    if (event.type == SDL_MOUSEBUTTONUP) ok = 1;
669 		}
670 		break;
671 	    }
672 	}
673 
674     } while (!ok);
675 
676     if (bg_surf) {
677 	SDL_BlitSurface(bg_surf, NULL, screen, NULL);
678 	SDL_FreeSurface(bg_surf);
679     }
680 }
681 
682 
683 int
show_menu(SDL_Surface * screen,char * title,int n_options,char * optiontxts[],int default_option)684 show_menu(SDL_Surface *screen, char *title, int n_options, char *optiontxts[], int default_option)
685 {
686     int ok = 0, i, tmpi;
687 
688     SDL_Rect dst, btn;
689 
690     int box_border = charset_charwid / 2;
691 
692     SDL_Event event;
693 
694     int hilight;
695     int got_input;
696     int min_wid;
697 
698     SDL_Surface *bg_surf = copysurface(screen);
699 
700     if (default_option < 0) default_option = 0;
701     else if (default_option >= n_options) default_option = n_options;
702 
703     hilight = default_option;
704     got_input = 0;
705 
706     min_wid = strlen(title);
707 
708     for (i = 0; i < n_options; i++) {
709 	tmpi = strlen(optiontxts[i]);
710 	if (tmpi > min_wid) min_wid = tmpi;
711     }
712 
713 
714     do {
715 
716 	dst.w = (min_wid*charset_charwid) + box_border*2;
717 
718 	dst.h = (charset_img->h * (2 + n_options)) + box_border*2;
719 
720 	dst.x = (SCREEN_WID/2) - (dst.w/2);
721 	dst.y = (SCREEN_HEI/2) - (dst.h/2);
722 
723 	if (bg_surf)
724 	    SDL_BlitSurface(bg_surf, NULL, screen, NULL);
725 
726 	draw_window(screen, dst.x, dst.y, dst.w, dst.h);
727 
728 	write_string(screen, -1, dst.y+box_border, title);
729 
730 	for (i = 0; i < n_options; i++) {
731 
732 	    btn.x = (SCREEN_WID/2) - ((min_wid*charset_charwid)/2);
733 	    btn.y = dst.y + charset_img->h*(2+i)+box_border;
734 	    btn.w = min_wid*charset_charwid;
735 	    btn.h = charset_img->h;
736 	    SDL_FillRect(screen, &btn, (hilight == i) ? button_hilite_color : button_color);
737 
738 	    write_string(screen, -1, dst.y+charset_img->h*(2+i)+box_border, optiontxts[i]);
739 
740 	}
741 
742 	SDL_Flip(screen);
743 
744 	SDL_Delay(25);
745 
746 	while (SDL_PollEvent(&event)) {
747 	    switch(event.type) {
748 	    case SDL_QUIT:
749 		hilight = -1;
750 		do_quit = TRUE;
751 		ok = 1;
752 		break;
753 	    case SDL_KEYDOWN:
754 	    case SDL_KEYUP:
755 		switch (event.key.keysym.sym) {
756 		default: got_input = 0; break;
757 		case SDLK_SPACE:
758 		case SDLK_KP_ENTER:
759 		case SDLK_RETURN:
760 		    if (event.type == SDL_KEYUP && got_input &&
761 			hilight >= 0 && hilight < n_options) ok = 1;
762 		    else got_input = 1;
763 		    break;
764 		case SDLK_ESCAPE:
765 		    if (event.type == SDL_KEYUP) {
766 			hilight = -1;
767 			ok = 1;
768 		    }
769 		    break;
770 		case SDLK_UP:
771 		    if (event.type == SDL_KEYUP) {
772 			if (hilight > 0) hilight--;
773 			else hilight = 0;
774 		    }
775 		    got_input = 0;
776 		    break;
777 		case SDLK_DOWN:
778 		    if (event.type == SDL_KEYUP) {
779 			if (hilight < n_options-1) hilight++;
780 			else hilight = n_options-1;
781 		    }
782 		    got_input = 0;
783 		    break;
784 		}
785 		break;
786 	    case SDL_MOUSEMOTION:
787 		for (i = 0; i < n_options; i++) {
788 		    int minx = ((SCREEN_WID/2) - ((min_wid*charset_charwid)/2));
789 		    int miny = (((SCREEN_HEI/2) - ((charset_img->h * (2 + n_options))/2 + box_border)) + charset_img->h*(2+i)) + box_border;
790 		    int wid = (min_wid*charset_charwid);
791 		    if ((event.motion.x >= minx) &&
792 			(event.motion.y >= miny) &&
793 			(event.motion.x <= minx+wid) &&
794 			(event.motion.y <= miny+charset_img->h)) {
795 			hilight = i;
796 			break;
797 		    }
798 		}
799 		break;
800 	    case SDL_MOUSEBUTTONUP:
801 	    case SDL_MOUSEBUTTONDOWN:
802 		for (i = 0; i < n_options; i++) {
803 		    int minx = ((SCREEN_WID/2) - ((min_wid*charset_charwid)/2));
804 		    int miny = (((SCREEN_HEI/2) - ((charset_img->h * (2 + n_options))/2 + box_border)) + charset_img->h*(2+i)) + box_border;
805 		    int wid = (min_wid*charset_charwid);
806 		    if ((event.motion.x >= minx) &&
807 			(event.motion.y >= miny) &&
808 			(event.motion.x <= minx+wid) &&
809 			(event.motion.y <= miny+charset_img->h)) {
810 			hilight = i;
811 			if (event.type == SDL_MOUSEBUTTONDOWN) got_input = 1;
812 			else if (event.type == SDL_MOUSEBUTTONUP && got_input) ok = 1;
813 			break;
814 		    }
815 		}
816 		break;
817 	    }
818 	}
819 
820     } while (!ok);
821 
822     if (bg_surf) {
823 	SDL_BlitSurface(bg_surf, NULL, screen, NULL);
824 	SDL_FreeSurface(bg_surf);
825     }
826 
827     return hilight;
828 }
829 
830 
831 void
show_gameover(SDL_Surface * screen,int state)832 show_gameover(SDL_Surface *screen, int state)
833 {
834     int ok = 0;
835 
836     int w = SCREEN_WID*2 / 3;
837     int h = SCREEN_HEI*2 / 3;
838 
839     int x = (SCREEN_WID/2) - (w/2);
840     int y = (SCREEN_HEI/2) - (h/2);
841 
842     SDL_Event event;
843 
844     char buf[64];
845 
846     int mbtn = 0;
847 
848     SDL_Surface *bg_surf = copysurface(screen);
849 
850     do {
851 
852 	if (bg_surf)
853 	    SDL_BlitSurface(bg_surf, NULL, screen, NULL);
854 
855 	draw_window(screen, x, y, w, h);
856 
857 
858 	switch (state) {
859 	default:
860 	case 0:
861 	    write_string(screen, -1, x+charset_img->h, "Game Over");
862 	    break;
863 	case 1:
864 	    write_string(screen, -1, x+charset_img->h,   "Congrats!");
865 	    write_string(screen, -1, x+charset_img->h*2, "You finished the level set!");
866 	    break;
867 	case 2:
868 	    write_string(screen, -1, x+charset_img->h, "Prepare for next level");
869 	    break;
870 	}
871 
872 	sprintf(buf, "Levelset: %s", levelset_name);
873 	write_string(screen, -1, x+charset_img->h*4, buf);
874 
875 
876 	sprintf(buf, "Score: %6i", score);
877 	write_string(screen, -1, x+charset_img->h*6, buf);
878 
879 	sprintf(buf, "Level: %6i", level);
880 	write_string(screen, -1, x+charset_img->h*7, buf);
881 
882 	sprintf(buf, "Moves: %6i", moves);
883 	write_string(screen, -1, x+charset_img->h*8, buf);
884 
885 
886 	SDL_Flip(screen);
887 
888 	SDL_Delay(25);
889 
890 	while (SDL_PollEvent(&event)) {
891 	    switch(event.type) {
892 	    case SDL_QUIT:
893 		ok = 1;
894 		break;
895 	    case SDL_KEYUP:
896 		ok = 1;
897 		break;
898 	    case SDL_MOUSEBUTTONDOWN:
899 		mbtn = 1;
900 	    case SDL_MOUSEBUTTONUP:
901 		if (mbtn == 1)
902 		    ok = 1;
903 		else mbtn = 0;
904 		break;
905 	    default:
906 		break;
907 	    }
908 	}
909 
910     } while (!ok);
911 
912     if (bg_surf) {
913 	SDL_BlitSurface(bg_surf, NULL, screen, NULL);
914 	SDL_FreeSurface(bg_surf);
915     }
916 
917 }
918 
919 
920 
921 void
print_end_of_game_info()922 print_end_of_game_info()
923 {
924     printf("Your score: %i\n", score);
925     printf("Level: %i\n", level);
926     printf("Moves: %i\n", moves);
927 }
928 
929 
930 int
random_used_tiletype()931 random_used_tiletype()
932 {
933     if (used_tiletypes_count)
934 	return (used_tiletypes[rand() % used_tiletypes_count]);
935     else return 0;
936 }
937 
938 void
pline(int lvl,char * fmt,...)939 pline(int lvl, char *fmt, ...)
940 {
941     if (lvl < debugging_level) {
942 	va_list args;
943 	va_start(args, fmt);
944 	vprintf(fmt, args);
945 	va_end(args);
946     }
947 }
948 
949 
950 void
borders_init()951 borders_init()
952 {
953     int x,y,z;
954 
955     used_tiletypes_count = 0;
956 
957     for (x = 0; x < MAP_WID; x++)
958 	for (y = 0; y < MAP_HEI; y++) {
959 	    int block = map[x][y].block;
960 	    if (block) {
961 		int exists = 0;
962 		for (z = 0; z < used_tiletypes_count; z++) {
963 		    if (used_tiletypes[z] == block) exists = 1;
964 		}
965 		if (!exists) {
966 		    used_tiletypes[used_tiletypes_count] = block;
967 		    used_tiletypes_count++;
968 		}
969 	    }
970 	}
971 
972     memset(border_n, 0, sizeof(border_n));
973     memset(border_s, 0, sizeof(border_s));
974     memset(border_e, 0, sizeof(border_e));
975     memset(border_w, 0, sizeof(border_w));
976 
977     for (x = 0; x < MAP_BORDER_WID; x++) {
978 	for (y = 0; y < MAP_WID; y++) {
979 	    border_n[x][y].dir = border_s[x][y].dir = DIR_NONE;
980 	    border_n[x][y].block = border_s[x][y].block = 1;
981 	}
982 	for (y = 0; y < MAP_HEI; y++) {
983 	    border_w[x][y].dir = border_e[x][y].dir = DIR_NONE;
984 	    border_w[x][y].block = border_e[x][y].block = 1;
985 	}
986     }
987 
988     if (used_tiletypes_count) {
989 	for (x = 0; x < MAP_BORDER_WID; x++) {
990 	    for (y = 0; y < MAP_WID; y++) {
991 		border_n[x][y].block = random_used_tiletype();
992 		border_s[x][y].block = random_used_tiletype();
993 	    }
994 	    for (y = 0; y < MAP_HEI; y++) {
995 		border_w[x][y].block = random_used_tiletype();
996 		border_e[x][y].block = random_used_tiletype();
997 	    }
998 	}
999     }
1000 }
1001 
1002 
1003 
1004 void
map_set_block(int x,int y,int tile,int dir)1005 map_set_block(int x, int y, int tile, int dir)
1006 {
1007     map[x][y].block = tile;
1008     map[x][y].dir = dir;
1009     map[x][y].xofs = map[x][y].yofs = map[x][y].xadj = map[x][y].yadj = map[x][y].steps = 0;
1010 }
1011 
1012 
1013 
1014 void
map_init()1015 map_init()
1016 {
1017     int x, y;
1018 
1019     for (x = 0; x < MAP_WID; x++)
1020 	for (y = 0; y < MAP_HEI; y++)
1021 	    map_set_block(x,y, 0, DIR_NONE);
1022 }
1023 
1024 int
map_corners_full()1025 map_corners_full()
1026 {
1027     if ((map[0][0].block) && (map[0][MAP_HEI-1].block) &&
1028 	(map[MAP_WID-1][MAP_HEI-1].block) && (map[MAP_WID-1][0].block))
1029 	return 1;
1030     return 0;
1031 }
1032 
1033 int
map_is_full()1034 map_is_full()
1035 {
1036     int x, y;
1037     for (x = 0; x < MAP_WID; x++) {
1038 	if (!(map[x][0].block)) return 0;
1039 	if (!(map[x][MAP_HEI-1].block)) return 0;
1040     }
1041     for (y = 0; y < MAP_HEI; y++) {
1042 	if (!(map[0][y].block)) return 0;
1043 	if (!(map[MAP_WID-1][y].block)) return 0;
1044     }
1045     return 1;
1046 }
1047 
1048 int
map_is_empty()1049 map_is_empty()
1050 {
1051     int x, y;
1052     for (x = 0; x < MAP_WID; x++)
1053 	for (y = 0; y < MAP_HEI; y++)
1054 	    if (map[x][y].block) return 0;
1055     return 1;
1056 }
1057 
1058 void
map_mirror_h()1059 map_mirror_h()
1060 {
1061     int x, y;
1062     for (x = 0; x < MAP_WID; x++)
1063 	for (y = 0; y < ((MAP_HEI+1)/2); y++)
1064 	    map[x][y] = map[x][MAP_HEI-1-y];
1065 }
1066 
1067 void
map_mirror_v()1068 map_mirror_v()
1069 {
1070     int x, y;
1071     for (x = 0; x < ((MAP_WID+1)/2); x++)
1072 	for (y = 0; y < MAP_HEI; y++)
1073 	    map[x][y] = map[MAP_WID-1-x][y];
1074 }
1075 
1076 
1077 void
map_generate_random(int level,int maxlevel)1078 map_generate_random(int level, int maxlevel)
1079 {
1080     int num_diff_blocks = rand() % (2 + (int)((level*6)/maxlevel));
1081 
1082     int num_blocks = rand() % (3 + (int)((level*15)/maxlevel) + (rand() % 3));
1083 
1084     int i,x,y;
1085     int block;
1086 
1087     if (num_blocks < 3) num_blocks = 3;
1088     if (num_diff_blocks < 3) num_diff_blocks = 3;
1089 
1090     do {
1091 
1092 	do {
1093 	    for (i = 0; i < num_blocks; i++) {
1094 		do {
1095 		    block = (rand() % num_diff_blocks);
1096 		} while (!block);
1097 
1098 		do {
1099 		    x = (rand() % MAP_WID);
1100 		    y = (rand() % MAP_HEI);
1101 		} while (map[x][y].block);
1102 
1103 		map_set_block(x,y, block, DIR_NONE);
1104 	    }
1105 	} while (map_is_full() || map_corners_full());
1106 
1107 	if (!(rand() % 4)) map_mirror_h();
1108 	if (!(rand() % 4)) map_mirror_v();
1109 
1110 	borders_init();
1111 
1112     } while (used_tiletypes_count < 2);
1113 
1114 }
1115 
1116 
1117 
1118 void
levelfile_free()1119 levelfile_free()
1120 {
1121     if (level_file) {
1122 	free(level_file);
1123 	level_file = NULL;
1124 	level_file_len = -1;
1125     }
1126 }
1127 
1128 int
levelfile_load(char * fname)1129 levelfile_load(char *fname)
1130 {
1131     FILE *fin;
1132 
1133     char fnamebuf[128];
1134 
1135     snprintf(fnamebuf, 127, "/usr/local/share/brickshooter/levels/%s.dat", fname);
1136 
1137     levelfile_free();
1138 
1139     fin = fopen(fnamebuf, "r");
1140 
1141     if (!fin) return 0;
1142 
1143     fseek(fin, 0, SEEK_END);
1144     level_file_len = ftell(fin);
1145 
1146     level_file = (char *)malloc(level_file_len);
1147 
1148     fseek(fin, 0, SEEK_SET);
1149 
1150     fread(level_file, sizeof(char), level_file_len, fin);
1151 
1152     fclose(fin);
1153 
1154     return 1;
1155 }
1156 
1157 char *
levelfile_line(long * pos)1158 levelfile_line(long *pos)
1159 {
1160     static char buf[256];
1161 
1162     int i = 0;
1163 
1164     buf[0] = 0;
1165 
1166     while ((i < 255) && (((*pos) + i) < level_file_len)) {
1167 	buf[i] = level_file[*pos+i];
1168 	if (buf[i] == '\n' || buf[i] == '\r')
1169 	    break;
1170 	i++;
1171     }
1172     buf[i] = 0;
1173     *pos += (i+2);
1174     return buf;
1175 }
1176 
1177 
1178 int
map_load(int level)1179 map_load(int level)
1180 {
1181     static boolean first_load = 1;
1182 
1183     long fpos = 0;
1184 
1185     int lvlcount = 0;
1186 
1187     int x, y;
1188 
1189     char tmpmap[MAP_WID][MAP_HEI];
1190 
1191     int in_map = 0;
1192     int map_max_x = 0;
1193     int map_max_y = 0;
1194     int adj_x = 0;
1195     int adj_y = 0;
1196 
1197     int tmpint;
1198     char tmpstr[256];
1199     char *buf;
1200 
1201     boolean got_level = FALSE;
1202 
1203     if (random_levelset > 0) {
1204 	if (level <= random_levelset) {
1205 	    map_generate_random(level, random_levelset);
1206 	    return 1;
1207 	} else return 0;
1208     }
1209 
1210 
1211     if ((level_file_len < 0) || (level_file == NULL)) return 0;
1212 
1213     for (x = 0; x < MAP_WID; x++)
1214 	for (y = 0; y < MAP_HEI; y++)
1215 	    tmpmap[x][y] = 0;
1216 
1217     map_init();
1218 
1219     y = 0;
1220 
1221     while (fpos < level_file_len) {
1222 
1223 	buf = levelfile_line(&fpos);
1224 
1225 	if (buf[0] == '#') continue;
1226 	if (buf[strlen(buf)-1] == 10)
1227 	    buf[strlen(buf)-1] = 0;
1228 
1229 	if (first_load) {
1230 	    if (!in_map) {
1231 		if (strstr(buf, "SETNAME:") == buf) {
1232 		    char *parm = strchr(buf, ':');
1233 		    parm++;
1234 		    strcpy(levelset_name, parm);
1235 		}
1236 	    }
1237 	}
1238 
1239 	if (lvlcount == level) {
1240 
1241 	    if (in_map) {
1242 		if (!strcmp(buf, "ENDMAP")) {
1243 		    if (map_max_y < y) map_max_y = y;
1244 		    in_map = 0;
1245 		    got_level = TRUE;
1246 		} else {
1247 		    if (y < MAP_HEI) {
1248 			for (x = 0; x < strlen(buf); x++) {
1249 			    if (buf[x] >= '0' && buf[x] <= '9')
1250 				tmpmap[x][y] = buf[x]-'0'+1;
1251 			}
1252 			if (map_max_x < strlen(buf)) map_max_x = strlen(buf);
1253 			y++;
1254 		    }
1255 		}
1256 	    } else {
1257 		if (!strcmp(buf, "LEVEL")) break;
1258 		else if (!strcmp(buf, "MAP")) in_map = 1;
1259 		else if (sscanf(buf, "BACKGROUND:%s", tmpstr) == 1) {
1260 		    SDL_Surface *tmpsurf = IMG_Load(tmpstr);
1261 		    if (tmpsurf) {
1262 			if (background) SDL_FreeSurface(background);
1263 			background = tmpsurf;
1264 		    }
1265 		}
1266 		else if (sscanf(buf, "SEED:%d", &tmpint) == 1) {
1267 		    srand(tmpint);
1268 		}
1269 	    }
1270 
1271 	} else if (!strcmp(buf, "LEVEL")) lvlcount++;
1272     }
1273 
1274     if (!got_level) return 0;
1275 
1276     if (map_max_x < MAP_WID) adj_x = ((MAP_WID - map_max_x) / 2);
1277     if (map_max_y < MAP_HEI) adj_y = ((MAP_HEI - map_max_y) / 2);
1278 
1279     for (x = 0; x < MAP_WID-adj_x; x++)
1280 	for (y = 0; y < MAP_HEI-adj_y; y++) {
1281 	    map_set_block(x+adj_x, y+adj_y, tmpmap[x][y], DIR_NONE);
1282 	}
1283 
1284     first_load = 0;
1285 
1286     return 1;
1287 }
1288 
1289 
1290 int
can_fire(int * len)1291 can_fire(int *len)
1292 {
1293     int ok = 0;
1294     int x;
1295     int tile;
1296     int length = 0;
1297 
1298     switch (cursor_side) {
1299     default:
1300     case DIR_N:
1301 	for (x = 0; x < MAP_HEI; x++) {
1302 	    tile = (map[cursor_pos][x].block);
1303 	    if (tile) { ok = 1; break; }
1304 	    length++;
1305 	}
1306 	break;
1307     case DIR_E:
1308 	for (x = MAP_WID-1; x >= 0; x--) {
1309 	    tile = (map[x][cursor_pos].block);
1310 	    if (tile) { ok = 1; break; }
1311 	    length++;
1312 	}
1313 	break;
1314     case DIR_S:
1315 	for (x = MAP_HEI-1; x >= 0; x--) {
1316 	    tile = (map[cursor_pos][x].block);
1317 	    if (tile) { ok = 1; break; }
1318 	    length++;
1319 	}
1320 	break;
1321     case DIR_W:
1322 	for (x = 0; x < MAP_WID; x++) {
1323 	    tile = (map[x][cursor_pos].block);
1324 	    if (tile) { ok = 1; break; }
1325 	    length++;
1326 	}
1327 	break;
1328     }
1329 
1330     if (ok && (length == 0)) ok = 0;
1331 
1332     if (ok && len) *len = length;
1333     return ok;
1334 }
1335 
1336 
1337 void
map_draw(int parts)1338 map_draw(int parts)
1339 {
1340     int x, y;
1341 
1342     int map_left = (SCREEN_WID / 2) - ((MAP_WID / 2)*TILE_WID) - ((MAP_WID*TILE_SPACE)/2);
1343     int map_top = (SCREEN_HEI / 2) - ((MAP_HEI / 2)*TILE_HEI) - ((MAP_HEI*TILE_SPACE)/2);
1344 
1345     SDL_Rect r;
1346     SDL_Rect src;
1347 
1348     int tile;
1349     int dir;
1350     int ok;
1351     int length;
1352 
1353     char scorebuf[32];
1354 
1355     if (background) {
1356 	SDL_BlitSurface(background, NULL, screen, NULL);
1357     } else {
1358 	SDL_FillRect(screen, NULL, bg_color);
1359     }
1360 
1361     ok = 0;
1362     length = 0;
1363 
1364     if (!(parts & 1) && cursor_shown) {
1365 
1366 	int adj_x = 0;
1367 	int adj_y = 0;
1368 
1369 	/* the bar that shows where we'll hit */
1370 	switch (cursor_side) {
1371 	default:
1372 	case DIR_N:
1373 	    ok = can_fire(&length);
1374 	    if (ok && length) {
1375 		r.h = length * (TILE_HEI + TILE_SPACE) - TILE_SPACE;
1376 		r.w = TILE_WID;
1377 		r.x = map_left + cursor_pos * (TILE_WID + TILE_SPACE);
1378 		r.y = map_top;
1379 		adj_y = TILE_HEI;
1380 	    }
1381 	    break;
1382 	case DIR_E:
1383 	    ok = can_fire(&length);
1384 	    if (ok && length) {
1385 		r.w = (length) * (TILE_WID + TILE_SPACE) - TILE_SPACE;
1386 		r.h = TILE_HEI;
1387 		r.y = map_top + cursor_pos * (TILE_WID + TILE_SPACE);
1388 		r.x = map_left + (MAP_WID-length) * (TILE_WID + TILE_SPACE);
1389 		adj_x = TILE_WID;
1390 	    }
1391 	    break;
1392 	case DIR_S:
1393 	    ok = can_fire(&length);
1394 	    if (ok && length) {
1395 		r.h = (length) * (TILE_HEI + TILE_SPACE) - TILE_SPACE;
1396 		r.w = TILE_WID;
1397 		r.x = map_left + cursor_pos * (TILE_WID + TILE_SPACE);
1398 		r.y = map_top + (MAP_HEI-length) * (TILE_HEI + TILE_SPACE);
1399 		adj_y = TILE_HEI;
1400 	    }
1401 	    break;
1402 	case DIR_W:
1403 	    ok = can_fire(&length);
1404 	    if (ok && length) {
1405 		r.w = length * (TILE_HEI + TILE_SPACE) - TILE_SPACE;
1406 		r.h = TILE_WID;
1407 		r.y = map_top + cursor_pos * (TILE_WID + TILE_SPACE);
1408 		r.x = map_left;
1409 		adj_x = TILE_WID;
1410 	    }
1411 	    break;
1412 	}
1413 
1414 	if (target_pointer_img) {
1415 
1416 	    src.w = TILE_WID;
1417 	    src.h = TILE_HEI;
1418 	    src.x = cursor_side*TILE_WID;
1419 
1420 	    if (length == 1) {
1421 		src.y = 0;
1422 		SDL_BlitSurface(target_pointer_img, &src, screen, &r);
1423 	    } else if (length == 2) {
1424 		src.y = TILE_HEI;
1425 		SDL_BlitSurface(target_pointer_img, &src, screen, &r);
1426 		src.y = TILE_HEI*3;
1427 		r.x += adj_x;
1428 		r.y += adj_y;
1429 		SDL_BlitSurface(target_pointer_img, &src, screen, &r);
1430 	    } else if (length) {
1431 
1432 		int tmpi;
1433 
1434 		src.y = TILE_HEI;
1435 		SDL_BlitSurface(target_pointer_img, &src, screen, &r);
1436 
1437 		src.y = TILE_HEI*2;
1438 		for (tmpi = 0; tmpi < length - 2; tmpi++) {
1439 		    r.x += adj_x;
1440 		    r.y += adj_y;
1441 		    SDL_BlitSurface(target_pointer_img, &src, screen, &r);
1442 		}
1443 
1444 		src.y = TILE_HEI*3;
1445 		r.x += adj_x;
1446 		r.y += adj_y;
1447 		SDL_BlitSurface(target_pointer_img, &src, screen, &r);
1448 	    }
1449 	} else {
1450 	    SDL_FillRect(screen, &r, allow_color);
1451 	}
1452     }
1453 
1454 
1455     r.w = TILE_WID;
1456     r.h = TILE_HEI;
1457 
1458     src.w = TILE_WID;
1459     src.h = TILE_HEI;
1460 
1461     for (x = 0; x < MAP_WID; x++)
1462 	for (y = 0; y < MAP_HEI; y++)
1463 	    if (map[x][y].block) {
1464 		r.x = map_left + (x * (TILE_WID + TILE_SPACE));
1465 		r.y = map_top + (y * (TILE_HEI + TILE_SPACE));
1466 
1467 		tile = (map[x][y].block);
1468 		dir = (map[x][y].dir);
1469 
1470 		r.x += map[x][y].xofs;
1471 		r.y += map[x][y].yofs;
1472 
1473 		src.x = dir * TILE_WID;
1474 		src.y = (tile-1) * TILE_HEI;
1475 		SDL_BlitSurface(tilegfx, &src, screen, &r);
1476 	    }
1477 
1478     /* north border */
1479     for (x = 0; x < MAP_BORDER_WID; x++) {
1480 	for (y = 0; y < MAP_WID; y++) {
1481 	    r.x = map_left + (y * (TILE_WID + TILE_SPACE));
1482 	    r.y = map_top - ((x+1) * (TILE_HEI + TILE_SPACE));
1483 	    tile = border_n[x][y].block;
1484 	    dir = border_n[x][y].dir;
1485 	    src.x = dir * TILE_WID;
1486 	    src.y = (tile-1) * TILE_HEI;
1487 	    SDL_BlitSurface(tilegfx, &src, screen, &r);
1488 	}
1489     }
1490 
1491     /* south border */
1492     for (x = 0; x < MAP_BORDER_WID; x++) {
1493 	for (y = 0; y < MAP_WID; y++) {
1494 	    r.x = map_left + (y * (TILE_WID + TILE_SPACE));
1495 	    r.y = map_top + (MAP_HEI * TILE_HEI) + (TILE_SPACE * MAP_HEI) + (x * (TILE_HEI + TILE_SPACE));
1496 	    tile = (border_s[x][y].block);
1497 	    dir = (border_s[x][y].dir);
1498 	    src.x = dir * TILE_WID;
1499 	    src.y = (tile-1) * TILE_HEI;
1500 	    SDL_BlitSurface(tilegfx, &src, screen, &r);
1501 	}
1502     }
1503 
1504     /* west border */
1505     for (x = 0; x < MAP_BORDER_WID; x++) {
1506 	for (y = 0; y < MAP_WID; y++) {
1507 	    r.x = map_left - ((x+1) * (TILE_WID + TILE_SPACE));
1508 	    r.y = map_top + (y * (TILE_HEI + TILE_SPACE));
1509 	    tile = (border_w[x][y].block);
1510 	    dir = (border_w[x][y].dir);
1511 	    src.x = dir * TILE_WID;
1512 	    src.y = (tile-1) * TILE_HEI;
1513 	    SDL_BlitSurface(tilegfx, &src, screen, &r);
1514 	}
1515     }
1516 
1517     /* east border */
1518     for (x = 0; x < MAP_BORDER_WID; x++) {
1519 	for (y = 0; y < MAP_WID; y++) {
1520 	    r.x = map_left + (MAP_WID*TILE_WID) + (TILE_SPACE*MAP_WID) +  (x * (TILE_WID + TILE_SPACE));
1521 	    r.y = map_top + (y * (TILE_HEI + TILE_SPACE));
1522 	    tile = (border_e[x][y].block);
1523 	    dir = (border_e[x][y].dir);
1524 	    src.x = dir * TILE_WID;
1525 	    src.y = (tile-1) * TILE_HEI;
1526 	    SDL_BlitSurface(tilegfx, &src, screen, &r);
1527 	}
1528     }
1529 
1530 
1531 
1532     /* cursor */
1533     if (!(parts & 1) && cursor_shown) {
1534 
1535 	r.w = TILE_WID + (TILE_SPACE*2);
1536 	r.h = TILE_HEI + (TILE_SPACE*2);
1537 
1538 	cursor_side = cursor_side % DIR_NONE;
1539 	switch (cursor_side) {
1540 	default:
1541 	case DIR_N:
1542 	    if (cursor_pos < 0) cursor_pos += MAP_WID;
1543 	    cursor_pos = (cursor_pos % MAP_WID);
1544 	    r.x = map_left + cursor_pos * (TILE_WID + TILE_SPACE);
1545 	    r.y = map_top - (TILE_HEI + TILE_SPACE);
1546 	    break;
1547 	case DIR_E:
1548 	    if (cursor_pos < 0) cursor_pos += MAP_HEI;
1549 	    cursor_pos = (cursor_pos % MAP_HEI);
1550 	    r.x = map_left + (MAP_WID*TILE_WID)+(TILE_SPACE*MAP_WID);
1551 	    r.y = map_top + cursor_pos * (TILE_WID + TILE_SPACE);
1552 	    break;
1553 	case DIR_S:
1554 	    if (cursor_pos < 0) cursor_pos += MAP_WID;
1555 	    cursor_pos = (cursor_pos % MAP_WID);
1556 	    r.x = map_left + cursor_pos * (TILE_WID + TILE_SPACE);
1557 	    r.y = map_top + (MAP_HEI*TILE_HEI) + (MAP_HEI*TILE_SPACE);
1558 	    break;
1559 	case DIR_W:
1560 	    if (cursor_pos < 0) cursor_pos += MAP_HEI;
1561 	    cursor_pos = (cursor_pos % MAP_HEI);
1562 	    r.x = map_left - (TILE_WID+TILE_SPACE);
1563 	    r.y = map_top + cursor_pos * (TILE_WID + TILE_SPACE);
1564 	    break;
1565 	}
1566 
1567 	r.x -= TILE_SPACE;
1568 	r.y -= TILE_SPACE;
1569 
1570 	if (cursor_img) {
1571 	    r.x += ((TILE_WID+TILE_SPACE-(cursor_img->w/2)+1)/2);
1572 	    r.y += ((TILE_HEI+TILE_SPACE-(cursor_img->h/4)+1)/2);
1573 
1574 	    src.w = (cursor_img->w / 2);
1575 	    src.h = (cursor_img->h / 4);
1576 	    src.x = can_fire(NULL)*(src.w);
1577 	    src.y = cursor_side * src.h;
1578 	    SDL_BlitSurface(cursor_img, &src, screen, &r);
1579 	} else {
1580 	    SDL_FillRect(screen, &r, cursor_color);
1581 	}
1582 
1583     }
1584 
1585     explosions_draw(screen);
1586 
1587 
1588     /* score */
1589     if (score_bg_img) {
1590 	r.x = SCORE_BG_X;
1591 	r.y = SCORE_BG_Y;
1592 	SDL_BlitSurface(score_bg_img, NULL, screen, &r);
1593     }
1594     snprintf(scorebuf, 30, "%6i", score);
1595     write_string(screen, SCORE_X, SCORE_Y, scorebuf);
1596 
1597     /* level */
1598     if (leveltxt_bg_img) {
1599 	r.x = LEVELTXT_BG_X;
1600 	r.y = LEVELTXT_BG_Y;
1601 	SDL_BlitSurface(leveltxt_bg_img, NULL, screen, &r);
1602     }
1603     snprintf(scorebuf, 30, "%3i", level);
1604     write_string(screen, LEVEL_POS_X, LEVEL_POS_Y, scorebuf);
1605 }
1606 
1607 
1608 int
map_check_flood(int x,int y,int block)1609 map_check_flood(int x, int y, int block)
1610 {
1611     int coords_x[MAX_FLOOD_HEAP];
1612     int coords_y[MAX_FLOOD_HEAP];
1613     int coords_c = 0;
1614 
1615     int mapmask[MAP_WID][MAP_HEI];
1616 
1617     int dx,dy;
1618     int mask_count = 0;
1619     int checked = 0;
1620 
1621     for (dx = 0; dx < MAP_WID; dx++)
1622 	for (dy = 0; dy < MAP_HEI; dy++)
1623 	    mapmask[dx][dy] = 0;
1624 
1625     coords_x[coords_c] = x; coords_y[coords_c] = y; coords_c++;
1626 
1627     do {
1628 	coords_c--;
1629 	x = coords_x[coords_c]; y = coords_y[coords_c];
1630 
1631 	if (x >= 0 && y >= 0 && x < MAP_WID && y < MAP_HEI) {
1632 
1633 	    mapmask[x][y] = 1;
1634 	    mask_count++;
1635 
1636 	    if ((x+1 < MAP_WID) && ((map[x+1][y].block) == block) && (mapmask[x+1][y] == 0)) {
1637 		coords_x[coords_c] = (x+1); coords_y[coords_c] = y; coords_c++;
1638 	    }
1639 	    if ((x-1 >= 0) && ((map[x-1][y].block) == block) && (mapmask[x-1][y] == 0)) {
1640 		coords_x[coords_c] = (x-1); coords_y[coords_c] = y; coords_c++;
1641 	    }
1642 	    if ((y+1 < MAP_HEI) && ((map[x][y+1].block) == block) && (mapmask[x][y+1] == 0)) {
1643 		coords_x[coords_c] = x; coords_y[coords_c] = (y+1); coords_c++;
1644 	    }
1645 	    if ((y-1 >= 0) && ((map[x][y-1].block) == block) && (mapmask[x][y-1] == 0)) {
1646 		coords_x[coords_c] = x; coords_y[coords_c] = (y-1); coords_c++;
1647 	    }
1648 	}
1649     } while (coords_c && (coords_c < MAX_FLOOD_HEAP-4));
1650 
1651     if (mask_count >= 3) {
1652 	int i;
1653 	int tmp = 1;
1654 	if (mask_count > 3) {
1655 	    /* bonuses for destroying 3+ blocks: 3 -> 0pts, 4 -> 3pts, 5 -> 12pts, 6 -> 60pts, 7 -> 360pts */
1656 	    for (i = 3; i < mask_count; i++)
1657 		tmp = tmp * i;
1658 	    score += tmp;
1659 	}
1660 
1661 	for (dx = 0; dx < MAP_WID; dx++)
1662 	    for (dy = 0; dy < MAP_HEI; dy++)
1663 		if (mapmask[dx][dy]) {
1664 		    int map_left = (SCREEN_WID / 2) - ((MAP_WID / 2)*TILE_WID) - ((MAP_WID*TILE_SPACE)/2);
1665 		    int map_top = (SCREEN_HEI / 2) - ((MAP_HEI / 2)*TILE_HEI) - ((MAP_HEI*TILE_SPACE)/2);
1666 
1667 		    score += 10; /* 10pts per destroyed block */
1668 		    map_set_block(dx, dy, 0, DIR_NONE);
1669 		    checked = 1;
1670 
1671 		    explosions_add(map_left + dx * (TILE_WID+TILE_SPACE), map_top + dy * (TILE_HEI+TILE_SPACE));
1672 		}
1673 
1674 	for (i = 2; i < mask_count; i++)
1675 	    play_sound(SND_EXPLODE);
1676     }
1677 
1678     return checked;
1679 }
1680 
1681 
1682 int
map_check()1683 map_check()
1684 {
1685     int x, y, block;
1686 
1687     int checked = 0;
1688 
1689     for (x = 0; x < MAP_WID; x++)
1690 	for (y = 0; y < MAP_HEI; y++) {
1691 	    block = (map[x][y].block);
1692 	    if (block)
1693 		checked |= map_check_flood(x,y, block);
1694 	}
1695     return checked;
1696 }
1697 
1698 void
map_shift_blocks()1699 map_shift_blocks()
1700 {
1701     int x,y;
1702 
1703     int done;
1704     int show_frame = 0;
1705 
1706     int frame_skips[3] = {9, 18, 36};
1707 
1708 
1709     do {
1710 	done = 1;
1711 	for (x = 0; x < MAP_WID; x++)
1712 	    for (y = 0; y < MAP_HEI; y++) {
1713 		if (map[x][y].steps > 0) {
1714 		    map[x][y].xofs += map[x][y].xadj;
1715 		    map[x][y].yofs += map[x][y].yadj;
1716 		    map[x][y].steps--;
1717 		    done = 0;
1718 		}
1719 	    }
1720 
1721 	if (anim_delay && !((++show_frame) % frame_skips[block_move_speed % 3])) {
1722 	    map_draw(1);
1723 	    explosions_anim();
1724 	    SDL_Flip(screen);
1725 	}
1726 
1727     } while (!done);
1728 
1729     if (anim_delay) {
1730 	map_draw(1);
1731 	explosions_anim();
1732 	SDL_Flip(screen);
1733     }
1734 
1735 }
1736 
1737 int
map_run(int * sounds)1738 map_run(int *sounds)
1739 {
1740     int x,y, block, dir, moved;
1741     int i;
1742 
1743     int checked = 0;
1744     boolean blockhit = 0;
1745     boolean blockout = 0;
1746 
1747     struct mapcell tmpmap[MAP_WID][MAP_HEI];
1748 
1749     do {
1750 
1751 	for (x = 0; x < MAP_WID; x++)
1752 	    for (y = 0; y < MAP_HEI; y++) {
1753 		tmpmap[x][y].block = map[x][y].block;
1754 		tmpmap[x][y].dir = map[x][y].dir;
1755 	    }
1756 
1757 	moved = 0;
1758 
1759 	for (x = 0; x < MAP_WID; x++)
1760 	    for (y = 0; y < MAP_HEI; y++)
1761 	    if (tmpmap[x][y].block) {
1762 		block = tmpmap[x][y].block;
1763 		dir = tmpmap[x][y].dir;
1764 		switch (dir) {
1765 		default:
1766 		case DIR_NONE: break;
1767 		case DIR_N:
1768 		    if (y-1 >= 0) {
1769 			if (!(map[x][y-1].block)) {
1770 			    map_set_block(x,y-1, block, dir);
1771 			    map[x][y-1].steps = (TILE_HEI+TILE_SPACE);
1772 			    map[x][y-1].yadj = -1;
1773 			    map[x][y-1].yofs = (TILE_HEI+TILE_SPACE);
1774 			    map_set_block(x, y, 0, DIR_NONE);
1775 			    moved = 1;
1776 			} else {
1777 			    blockhit = 1;
1778 			}
1779 		    } else {
1780 			for (i = MAP_BORDER_WID-2; i >= 0; i--)
1781 			    border_n[i+1][x] = border_n[i][x];
1782 			border_n[0][x].block = block;
1783 			border_n[0][x].dir = DIR_NONE;
1784 			map_set_block(x, y, 0, DIR_NONE);
1785 			moved = 1;
1786 			if (score >= 10) score -= 10;
1787 			blockout = 1;
1788 		    }
1789 		    break;
1790 		case DIR_S:
1791 		    if (y+1 < MAP_HEI) {
1792 			if (!(map[x][y+1].block)) {
1793 			    map_set_block(x,y+1, block, dir);
1794 			    map[x][y+1].steps = (TILE_HEI+TILE_SPACE);
1795 			    map[x][y+1].yadj = 1;
1796 			    map[x][y+1].yofs = -(TILE_HEI+TILE_SPACE);
1797 			    map_set_block(x, y, 0, DIR_NONE);
1798 			    moved = 1;
1799 			} else {
1800 			    blockhit = 1;
1801 			}
1802 		    } else {
1803 			for (i = MAP_BORDER_WID-2; i >= 0; i--)
1804 			    border_s[i+1][x] = border_s[i][x];
1805 			border_s[0][x].block = block;
1806 			border_s[0][x].dir = DIR_NONE;
1807 			map_set_block(x, y, 0, DIR_NONE);
1808 			moved = 1;
1809 			if (score >= 10) score -= 10;
1810 			blockout = 1;
1811 		    }
1812 		    break;
1813 		case DIR_W:
1814 		    if (x-1 >= 0) {
1815 			if (!(map[x-1][y].block)) {
1816 			    map_set_block(x-1,y, block, dir);
1817 			    map[x-1][y].steps = (TILE_WID+TILE_SPACE);
1818 			    map[x-1][y].xadj = -1;
1819 			    map[x-1][y].xofs = (TILE_WID+TILE_SPACE);
1820 			    map_set_block(x, y, 0, DIR_NONE);
1821 			    moved = 1;
1822 			} else {
1823 			    blockhit = 1;
1824 			}
1825 		    } else {
1826 			for (i = MAP_BORDER_WID-2; i >= 0; i--)
1827 			    border_w[i+1][y] = border_w[i][y];
1828 			border_w[0][y].block = block;
1829 			border_w[0][y].dir = DIR_NONE;
1830 			map_set_block(x, y, 0, DIR_NONE);
1831 			moved = 1;
1832 			if (score >= 10) score -= 10;
1833 			blockout = 1;
1834 		    }
1835 		    break;
1836 		case DIR_E:
1837 		    if (x+1 < MAP_WID) {
1838 			if (!(map[x+1][y].block)) {
1839 			    map_set_block(x+1,y, block, dir);
1840 			    map[x+1][y].steps = (TILE_WID+TILE_SPACE);
1841 			    map[x+1][y].xadj = 1;
1842 			    map[x+1][y].xofs = -(TILE_WID+TILE_SPACE);
1843 			    map_set_block(x, y, 0, DIR_NONE);
1844 			    moved = 1;
1845 			} else {
1846 			    blockhit = 1;
1847 			}
1848 		    } else {
1849 			for (i = MAP_BORDER_WID-2; i >= 0; i--)
1850 			    border_e[i+1][y] = border_e[i][y];
1851 			border_e[0][y].block = block;
1852 			border_e[0][y].dir = DIR_NONE;
1853 			map_set_block(x, y, 0, DIR_NONE);
1854 			moved = 1;
1855 			if (score >= 10) score -= 10;
1856 			blockout = 1;
1857 		    }
1858 		    break;
1859 		}
1860 	    }
1861 
1862 	map_shift_blocks();
1863 
1864 	/*
1865 	if (anim_delay) {
1866 	    map_draw(1);
1867 	    explosions_anim();
1868 	    SDL_Flip(screen);
1869 	    SDL_Delay(anim_delay);
1870 	    }
1871 	*/
1872 
1873 	checked |= moved;
1874     } while (moved);
1875 
1876 
1877     if (sounds) {
1878 	*sounds = 0;
1879 	if (blockhit) *sounds = (*sounds) + 1;
1880 	if (blockout) *sounds = (*sounds) + 2;
1881     }
1882 
1883     return checked;
1884 }
1885 
1886 
1887 
1888 
1889 void
exit_game()1890 exit_game()
1891 {
1892     pline(2, "Shutting down SDL.\n");
1893 
1894     SDL_FreeSurface(screen);
1895     SDL_FreeSurface(background);
1896     SDL_FreeSurface(window_border);
1897     SDL_FreeSurface(cursor_img);
1898     SDL_FreeSurface(target_pointer_img);
1899     SDL_FreeSurface(score_bg_img);
1900     SDL_FreeSurface(leveltxt_bg_img);
1901     SDL_FreeSurface(charset_img);
1902     SDL_FreeSurface(tilegfx);
1903 
1904     SDL_Quit();
1905     printf("Bye!\n");
1906     print_end_of_game_info();
1907 
1908     levelfile_free();
1909 }
1910 
1911 
1912 void
init_game()1913 init_game()
1914 {
1915     int modeflags = SDL_HWSURFACE|SDL_ANYFORMAT;
1916 
1917     pline(2, "Initializing SDL.\n");
1918 
1919     if ((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) == -1)) {
1920 	printf("Could not initialize SDL: %s.\n", SDL_GetError());
1921 	exit(-1);
1922     }
1923     pline(2, "SDL initialized.\n");
1924 
1925     atexit(SDL_Quit);
1926 
1927     if (is_fullscreen) {
1928 	modeflags |= SDL_FULLSCREEN;
1929     }
1930 
1931     screen = SDL_SetVideoMode(SCREEN_WID,SCREEN_HEI, SCREEN_BPP, modeflags);
1932     if (screen == NULL) {
1933 	fprintf(stderr, "Couldn't set %dx%dx%d video mode: %s.\n",
1934 		SCREEN_WID, SCREEN_HEI, SCREEN_BPP,
1935 		SDL_GetError());
1936 	exit(1);
1937     }
1938 
1939     pline(2, "Set %dx%dx%d %s mode.\n", screen->w, screen->h,
1940 	  screen->format->BitsPerPixel,
1941 	  is_fullscreen ? "fullscreen" : "windowed" );
1942 
1943     SDL_WM_SetCaption(GAME_NAME, NULL);
1944 
1945     if (audio_use) {
1946 
1947 	if(Mix_OpenAudio(audio_rate, audio_format, audio_num_channels, audio_buffers) != 0) {
1948 	    fprintf(stderr, "Unable to initialize audio: %s\n", Mix_GetError());
1949 	    audio_use = FALSE;
1950 	} else {
1951 	    int tmpi;
1952 	    for (tmpi = 0; tmpi < NUM_SOUNDS; tmpi++) {
1953 		audio_sound[tmpi] = Mix_LoadWAV(audio_sound_files[tmpi]);
1954 	    }
1955 	}
1956     }
1957 
1958 
1959 
1960     button_color = SDL_MapRGB(screen->format, 120, 150, 170);
1961     button_hilite_color = SDL_MapRGB(screen->format, 170,200,220);
1962     window_color = SDL_MapRGB(screen->format, 128,100,169);
1963 
1964 
1965     cursor_color = SDL_MapRGB(screen->format, 255,255,255);
1966     allow_color = SDL_MapRGB(screen->format, 100,100,100);
1967     bg_color = SDL_MapRGB(screen->format, 0, 0, 0);
1968 
1969     background = IMG_Load("/usr/local/share/brickshooter/gfx/background.png");
1970 
1971     window_border = IMG_Load("/usr/local/share/brickshooter/gfx/window_border.png");
1972 
1973     cursor_img = IMG_Load("/usr/local/share/brickshooter/gfx/cursor.png");
1974 
1975     target_pointer_img = IMG_Load("/usr/local/share/brickshooter/gfx/targeting.png");
1976 
1977     score_bg_img = IMG_Load("/usr/local/share/brickshooter/gfx/score_bg.png");
1978     leveltxt_bg_img = IMG_Load("/usr/local/share/brickshooter/gfx/level_bg.png");
1979 
1980     charset_img = IMG_Load("/usr/local/share/brickshooter/gfx/charset.png");
1981     if (charset_img == NULL) {
1982 	fprintf(stderr, "Couldn't load charset: %s.\n",
1983 		SDL_GetError());
1984 	exit(1);
1985     }
1986 
1987     explosion_img = IMG_Load("/usr/local/share/brickshooter/gfx/explosion.png");
1988 
1989     tilegfx = IMG_Load("/usr/local/share/brickshooter/gfx/tiles.png");
1990 
1991     if (tilegfx == NULL) {
1992 	fprintf(stderr, "Couldn't load tiles: %s.\n",
1993 		SDL_GetError());
1994 	exit(1);
1995     }
1996 
1997 
1998     if (!levelfile_load(levelset_fname)) {
1999 	fprintf(stderr, "Couldn't load level file %s.\n", levelset_fname);
2000 	show_popup(screen, "Couldn't load level set.", 7);
2001     }
2002 
2003     if (!map_load(level)) {
2004 	map_generate_random(1,10);
2005     }
2006 
2007     borders_init();
2008 
2009     explosions_init();
2010 }
2011 
2012 
2013 void
do_block_anim()2014 do_block_anim()
2015 {
2016     SDL_Event event;
2017     int fini = 0;
2018     boolean skip_delays = 0;
2019     int sounds = 0;
2020 
2021     do {
2022 	if (!fini && anim_delay && !skip_delays) {
2023 	    map_draw(1);
2024 	    SDL_Flip(screen);
2025 	    SDL_Delay(anim_delay);
2026 
2027 	    while (SDL_PollEvent(&event)) {
2028 		switch(event.type) {
2029 		default:
2030 		    break;
2031 		case SDL_QUIT:
2032 		    do_quit = TRUE;
2033 		    return;
2034 		case SDL_MOUSEBUTTONDOWN:
2035 		case SDL_KEYDOWN:
2036 		    skip_delays = 1;
2037 		    break;
2038 		}
2039 	    }
2040 
2041 	}
2042 	fini = map_run(&sounds);
2043 
2044 	if (!fini) {
2045 	    fini = map_check();
2046 	} else {
2047 	    if (sounds & 1)
2048 		play_sound(SND_BLOCKHIT);
2049 	    if (sounds & 2)
2050 		play_sound(SND_BLOCKOUT);
2051 	}
2052 
2053     } while (fini);
2054 }
2055 
2056 void
wait_for_explosions()2057 wait_for_explosions()
2058 {
2059     while (explosions_active()) {
2060 	explosions_anim();
2061 	explosions_draw(screen);
2062 	map_draw(2);
2063 	SDL_Flip(screen);
2064     }
2065 }
2066 
2067 void
next_level()2068 next_level()
2069 {
2070     wait_for_explosions();
2071     level++;
2072     explosions_init();
2073     if (map_load(level)) {
2074 	play_sound(SND_NEXTLEVEL);
2075 	show_gameover(screen, 2);
2076 	borders_init();
2077     } else {
2078 	play_sound(SND_FINISHSET);
2079 	show_gameover(screen, 1);
2080 	printf("Congratulations, you finished the level set!\n");
2081 	print_end_of_game_info();
2082 	program_state = 0;
2083     }
2084 }
2085 
2086 void
pressed_fire()2087 pressed_fire()
2088 {
2089     int length;
2090 
2091     if (can_fire(&length) && (length > 0)) {
2092 	int x;
2093 	moves++;
2094 	switch (cursor_side) {
2095 	case DIR_N:
2096 	    length--;
2097 	    map_set_block(cursor_pos, 0, border_n[0][cursor_pos].block, DIR_S);
2098 	    for (x = 0; x < MAP_BORDER_WID-1; x++)
2099 		border_n[x][cursor_pos] = border_n[x+1][cursor_pos];
2100 	    border_n[MAP_BORDER_WID-1][cursor_pos].block = random_used_tiletype();
2101 	    break;
2102 	case DIR_E:
2103 	    length--;
2104 	    map_set_block(MAP_WID-1, cursor_pos, border_e[0][cursor_pos].block, DIR_W);
2105 	    for (x = 0; x < MAP_BORDER_WID-1; x++)
2106 		border_e[x][cursor_pos] = border_e[x+1][cursor_pos];
2107 	    border_e[MAP_BORDER_WID-1][cursor_pos].block = random_used_tiletype();
2108 	    break;
2109 	case DIR_S:
2110 	    length--;
2111 	    map_set_block(cursor_pos, MAP_HEI-1, border_s[0][cursor_pos].block, DIR_N);
2112 	    for (x = 0; x < MAP_BORDER_WID-1; x++)
2113 		border_s[x][cursor_pos] = border_s[x+1][cursor_pos];
2114 	    border_s[MAP_BORDER_WID-1][cursor_pos].block = random_used_tiletype();
2115 	    break;
2116 	case DIR_W:
2117 	    length--;
2118 	    map_set_block(0, cursor_pos, border_w[0][cursor_pos].block, DIR_E);
2119 	    for (x = 0; x < MAP_BORDER_WID-1; x++)
2120 		border_w[x][cursor_pos] = border_w[x+1][cursor_pos];
2121 	    border_w[MAP_BORDER_WID-1][cursor_pos].block = random_used_tiletype();
2122 	    break;
2123 	}
2124 
2125 	play_sound(SND_SHOOTBLOCK);
2126 
2127 	do_block_anim();
2128 
2129 	if (map_is_empty()) {
2130 	    next_level();
2131 	} else if (map_is_full()) {
2132 	    play_sound(SND_GAMEOVER);
2133 	    wait_for_explosions();
2134 	    show_gameover(screen, 0);
2135 	    program_state = 0;
2136 	}
2137     } else {
2138 	play_sound(SND_CANTMOVE);
2139     }
2140 }
2141 
2142 void
options_menu_loop()2143 options_menu_loop()
2144 {
2145 #define OPTIONS_OPTS 4
2146     int return_from_options = 0;
2147     int i, def_opt = OPTIONS_OPTS-1;
2148     int selected_opt;
2149     char *optionsmenu[OPTIONS_OPTS];
2150 
2151     char *blockmovespeed_str[3] = {"slow", "normal", "fast"};
2152 
2153     for (i = 0; i < OPTIONS_OPTS; i++)
2154 	optionsmenu[i] = (char *)malloc(64);
2155 
2156     do {
2157 	cursor_shown = FALSE;
2158 	map_draw(1);
2159 	cursor_shown = TRUE;
2160 	sprintf(optionsmenu[0], "%s", (!is_fullscreen) ? "Fullscreen" : " Windowed ");
2161 	sprintf(optionsmenu[1], "Sounds: %3s", (audio_use) ? "on" : "off");
2162 	sprintf(optionsmenu[2], "Block speed: %6s", blockmovespeed_str[block_move_speed % 3]);
2163 	sprintf(optionsmenu[3], "Back");
2164 	selected_opt = show_menu(screen, "Options", OPTIONS_OPTS, optionsmenu, def_opt);
2165 	def_opt = selected_opt;
2166 	switch (selected_opt) {
2167 	case 0:
2168 	    if (SDL_WM_ToggleFullScreen(screen)) {
2169 		if (is_fullscreen)
2170 		    is_fullscreen = FALSE;
2171 		else
2172 		    is_fullscreen = TRUE;
2173 	    };
2174 	    break;
2175 	case 1:
2176 	    audio_use = !audio_use;
2177 	    break;
2178 	case 2:
2179 	    block_move_speed = (block_move_speed + 1) % 3;
2180 	    break;
2181 	default: return_from_options = 1; break;
2182 	}
2183     } while (!return_from_options);
2184 
2185     for (i = 0; i < OPTIONS_OPTS; i++)
2186 	free(optionsmenu[i]);
2187 
2188 }
2189 
2190 
2191 
2192 void
handle_key_down(SDL_keysym * keysym)2193 handle_key_down(SDL_keysym *keysym)
2194 {
2195     switch (keysym->sym) {
2196     case GKEY_FIRE:
2197 	pressed_fire();
2198 	break;
2199     case GKEY_TURN_CW:
2200 	{
2201 	    int length;
2202 	    if (can_fire(&length)) {
2203 		cursor_pos = length;
2204 		if (cursor_side == DIR_S)
2205 		    cursor_pos = MAP_WID - 1 - length;
2206 		else if (cursor_side == DIR_E)
2207 		    cursor_pos = MAP_HEI - 1 - length;
2208 	    }
2209 	    cursor_side = (cursor_side + 1) % 4;
2210 	}
2211 	break;
2212     case GKEY_TURN_CCW:
2213 	{
2214 	    int length;
2215 	    if (can_fire(&length)) {
2216 		cursor_pos = length;
2217 		if (cursor_side == DIR_S)
2218 		    cursor_pos = MAP_WID - 1 - length;
2219 		else if (cursor_side == DIR_E)
2220 		    cursor_pos = MAP_HEI - 1 - length;
2221 	    }
2222 	    cursor_side = (cursor_side + 3) % 4;
2223 	}
2224 	break;
2225     case GKEY_UP:
2226 	switch (cursor_side) {
2227 	case DIR_N: break;
2228 	case DIR_E:
2229 	    cursor_pos--;
2230 	    if (cursor_pos < 0) {
2231 		cursor_pos = MAP_WID-1;
2232 		cursor_side = 0;
2233 	    }
2234 	    break;
2235 	case DIR_S:
2236 	    cursor_side = 0;
2237 	    break;
2238 	case DIR_W:
2239 	    cursor_pos--;
2240 	    if (cursor_pos < 0) {
2241 		cursor_pos = 0;
2242 		cursor_side = 0;
2243 	    }
2244 	    break;
2245 	}
2246 	break;
2247     case GKEY_DOWN:
2248 	switch (cursor_side) {
2249 	case DIR_N:
2250 	    cursor_side = 2;
2251 	    break;
2252 	case DIR_E:
2253 	    cursor_pos++;
2254 	    if (cursor_pos >= MAP_HEI) {
2255 		cursor_pos = MAP_WID-1;
2256 		cursor_side = 2;
2257 	    }
2258 	    break;
2259 	case DIR_S: break;
2260 	case DIR_W:
2261 	    cursor_pos++;
2262 	    if (cursor_pos >= MAP_HEI) {
2263 		cursor_pos = 0;
2264 		cursor_side = 2;
2265 	    }
2266 	    break;
2267 	}
2268 	break;
2269     case GKEY_RIGHT:
2270 	switch (cursor_side) {
2271 	case DIR_N:
2272 	    cursor_pos++;
2273 	    if (cursor_pos >= MAP_WID) {
2274 		cursor_pos = 0;
2275 		cursor_side = 1;
2276 	    }
2277 	    break;
2278 	case DIR_E: break;
2279 	case DIR_S:
2280 	    cursor_pos++;
2281 	    if (cursor_pos >= MAP_WID) {
2282 		cursor_pos = MAP_HEI-1;
2283 		cursor_side = 1;
2284 	    }
2285 	    break;
2286 	case DIR_W:
2287 	    cursor_side = 1;
2288 	    break;
2289 	}
2290 	break;
2291     case GKEY_LEFT:
2292 	switch (cursor_side) {
2293 	case DIR_N:
2294 	    cursor_pos--;
2295 	    if (cursor_pos < 0) {
2296 		cursor_pos = 0;
2297 		cursor_side = 3;
2298 	    }
2299 	    break;
2300 	case DIR_E:
2301 	    cursor_side = 3;
2302 	    break;
2303 	case DIR_S:
2304 	    cursor_pos--;
2305 	    if (cursor_pos < 0) {
2306 		cursor_pos = MAP_HEI-1;
2307 		cursor_side = 3;
2308 	    }
2309 	    break;
2310 	case DIR_W: break;
2311 	}
2312 	break;
2313     case GKEY_QUIT:
2314 	{
2315 	    int quit_game_menu = 0;
2316 	    char *quitgamemenu[] = {
2317 		"Resume game",
2318 		"Options",
2319 		"Quit"
2320 	    };
2321 	    do {
2322 		cursor_shown = FALSE;
2323 		map_draw(1);
2324 		cursor_shown = TRUE;
2325 		switch (show_menu(screen, GAME_NAME, ARRAY_SIZE(quitgamemenu), quitgamemenu, 0)) {
2326 		default:
2327 		case 0: quit_game_menu = 1; break;
2328 		case 1: options_menu_loop(); break;
2329 		case 2:
2330 		    play_sound(SND_GAMEOVER);
2331 		    show_gameover(screen, 0);
2332 		    program_state = 0;
2333 		    quit_game_menu = 1;
2334 		    break;
2335 		}
2336 	    } while (!quit_game_menu);
2337 
2338 	}
2339 	break;
2340     default:
2341 	if (debugging_level && keysym->sym == 'n')
2342 	    next_level();
2343 	break;
2344     }
2345 }
2346 
2347 int
handle_mouse_hover(int mx,int my)2348 handle_mouse_hover(int mx, int my)
2349 {
2350     int map_left = (SCREEN_WID / 2) - ((MAP_WID / 2)*TILE_WID) - ((MAP_WID*TILE_SPACE)/2);
2351     int map_top = (SCREEN_HEI / 2) - ((MAP_HEI / 2)*TILE_HEI) - ((MAP_HEI*TILE_SPACE)/2);
2352 
2353     int x, y;
2354 
2355     int min_x, min_y, max_x, max_y;
2356 
2357     for (x = 0; x < MAP_WID; x++) {
2358 	min_x = map_left + x *(TILE_WID + TILE_SPACE);
2359 	min_y = map_top - (TILE_HEI+TILE_SPACE) * MAP_BORDER_WID;
2360 	max_x = min_x + TILE_WID;
2361 	max_y = min_y + ((TILE_HEI+TILE_SPACE) * MAP_BORDER_WID);
2362 
2363 	if (mx >= min_x && mx <= max_x && my >= min_y && my <= max_y) {
2364 	    cursor_side = DIR_N;
2365 	    cursor_pos = x;
2366 	    return 1;
2367 	}
2368 
2369 	min_y = map_top + MAP_HEI * (TILE_HEI+TILE_SPACE);
2370 	max_y = min_y + (TILE_HEI+TILE_SPACE)*MAP_BORDER_WID;
2371 
2372 	if (mx >= min_x && mx <= max_x && my >= min_y && my <= max_y) {
2373 	    cursor_side = DIR_S;
2374 	    cursor_pos = x;
2375 	    return 1;
2376 	}
2377 
2378     }
2379 
2380     for (y = 0; y < MAP_HEI; y++) {
2381 	min_y = map_top + y * (TILE_HEI + TILE_SPACE);
2382 	min_x = map_left - (TILE_WID + TILE_SPACE)*MAP_BORDER_WID;
2383 	max_x = min_x + (TILE_WID+TILE_SPACE)*MAP_BORDER_WID;
2384 	max_y = min_y + TILE_HEI;
2385 
2386 	if (mx >= min_x && mx <= max_x && my >= min_y && my <= max_y) {
2387 	    cursor_side = DIR_W;
2388 	    cursor_pos = y;
2389 	    return 1;
2390 	}
2391 
2392 	min_x = map_left + MAP_WID * (TILE_WID + TILE_SPACE);
2393 	max_x = min_x + (TILE_WID+TILE_SPACE)*MAP_BORDER_WID;
2394 
2395 	if (mx >= min_x && mx <= max_x && my >= min_y && my <= max_y) {
2396 	    cursor_side = DIR_E;
2397 	    cursor_pos = y;
2398 	    return 1;
2399 	}
2400 
2401     }
2402     return 0;
2403 }
2404 
2405 
2406 void
process_events()2407 process_events()
2408 {
2409     SDL_Event event;
2410 
2411     while (SDL_PollEvent(&event)) {
2412 	switch(event.type) {
2413 	case SDL_KEYUP:
2414 	    is_paused = FALSE;
2415 	    cursor_shown = TRUE;
2416 	    handle_key_down(&event.key.keysym);
2417 	    break;
2418 	case SDL_KEYDOWN:
2419 	    break;
2420 	case SDL_ACTIVEEVENT:
2421 	    if (event.active.state & (SDL_APPINPUTFOCUS|SDL_APPACTIVE)) {
2422 		is_paused = TRUE;
2423 	    }
2424 	    break;
2425 	case SDL_QUIT:
2426 	    fprintf(stdout, "Whee!\n");
2427 	    print_end_of_game_info();
2428 	    do_quit = TRUE;
2429 	    break;
2430 	case SDL_MOUSEMOTION:
2431 	    is_paused = FALSE;
2432 	    if (!handle_mouse_hover(event.motion.x, event.motion.y)) {
2433 		cursor_shown = FALSE;
2434 	    } else {
2435 		cursor_shown = TRUE;
2436 	    }
2437 	    break;
2438 	case SDL_MOUSEBUTTONDOWN:
2439 	    is_paused = FALSE;
2440 	    if (handle_mouse_hover(event.button.x, event.button.y)) {
2441 		cursor_shown = TRUE;
2442 		pressed_fire();
2443 	    }
2444 	case SDL_MOUSEBUTTONUP:
2445 	    is_paused = FALSE;
2446 	    if (handle_mouse_hover(event.button.x, event.button.y)) {
2447 		cursor_shown = TRUE;
2448 	    }
2449 	    break;
2450 	default:
2451 	    break;
2452 	}
2453     }
2454 }
2455 
2456 
2457 
2458 void
game_drawscreen_main()2459 game_drawscreen_main()
2460 {
2461     map_draw(0);
2462     SDL_Flip(screen);
2463     explosions_anim();
2464 }
2465 
2466 
2467 void
game_drawscreen_paused()2468 game_drawscreen_paused()
2469 {
2470     SDL_Flip(screen);
2471     SDL_Delay(200);
2472 }
2473 
2474 void
main_menu_loop()2475 main_menu_loop()
2476 {
2477     char *mainmenuoptions[] = {
2478 	"Play",
2479 	"Options",
2480 	"Quit"
2481     };
2482     int selected_opt = 0;
2483     static int def_opt = 0;
2484 
2485     cursor_shown = FALSE;
2486     map_draw(1);
2487     cursor_shown = TRUE;
2488     selected_opt = show_menu(screen, GAME_NAME, ARRAY_SIZE(mainmenuoptions), mainmenuoptions, def_opt);
2489     def_opt = selected_opt;
2490     switch (selected_opt) {
2491     case 0:
2492 	program_state = 1;
2493 	score = 0;
2494 	level = 1;
2495 	moves = 0;
2496 	play_sound(SND_STARTUP);
2497 	if (!map_load(level)) {
2498 	    map_generate_random(1,10);
2499 	}
2500 	break;
2501     case 1:
2502 	options_menu_loop();
2503 	break;
2504     default:
2505 	do_quit = TRUE;
2506 	break;
2507     }
2508 }
2509 
2510 void
main_game_loop()2511 main_game_loop()
2512 {
2513     while (!do_quit) {
2514 	if (is_paused) {
2515 	    game_drawscreen_paused();
2516 	    process_events();
2517 	} else {
2518 
2519 	    switch (program_state) {
2520 	    default:
2521 	    case 0:
2522 		main_menu_loop();
2523 		break;
2524 	    case 1:
2525 		/* playing */
2526 		game_drawscreen_main();
2527 		process_events();
2528 		break;
2529 	    }
2530 	}
2531     }
2532 }
2533 
2534 
2535 
2536 int
handle_params(int argc,char * argv[])2537 handle_params(int argc, char *argv[])
2538 {
2539   int i;
2540 
2541   for (i = 1; i < argc; i++) {
2542     char *str = argv[i];
2543     char *parm = strchr(str,'=');
2544 
2545     if (parm) parm++;
2546 
2547     if (strstr(str,"--help") == str || strstr(str, "-h") == str) {
2548 	printf("--help (or -h)\tthis help.\n");
2549 	printf("--level=X\tstart playing from level number X.\n");
2550 	printf("--levelset=name\tload levelset called 'name'.\n");
2551 	printf("--debug=X\tset debugging output level to X.\n");
2552 	printf("--fullscreen\tstart game in fullscreen mode.\n");
2553 	printf("--random=X\tplay a randomly generated levelset of X levels.\n");
2554 	printf("--nosound\tdisable sound effects.\n");
2555 	return 0;
2556     }
2557     else if (strstr(str,"--levelset") == str && parm) strncpy(levelset_fname, parm, 63);
2558     else if (strstr(str,"--level") == str && parm) {
2559 	level = atoi(parm);
2560 	debugging_level = 1;
2561     }
2562     else if (strstr(str,"--debug") == str) debugging_level = (parm ? atoi(parm) : 1);
2563     else if (strstr(str,"--nosound") == str) audio_use = FALSE;
2564     else if (strstr(str,"--fullscreen") == str) is_fullscreen = TRUE;
2565     else if (strstr(str,"--random") == str) {
2566 	if (parm) random_levelset = atoi(parm);
2567 	if (random_levelset < 1) random_levelset = 1;
2568 	strcpy(levelset_name, "Random Set");
2569     }
2570   }
2571 
2572   return 1;
2573 }
2574 
2575 
2576 
2577 int
main(int argc,char * argv[])2578 main(int argc, char *argv[])
2579 {
2580     printf("%s, %s.\n\n", GAME_NAME, GAME_VERSION);
2581 
2582     srand(time(NULL));
2583 
2584     strcpy(levelset_fname, def_levelset);
2585 
2586     if (handle_params(argc, argv)) {
2587 	init_game();
2588 	pline(2, "Running.\n");
2589 	main_game_loop();
2590 	exit_game();
2591     }
2592     return 0;
2593 }
2594