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