1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  */
12 
13 #include <stdio.h>
14 #include <SDL.h>
15 #include <SDL_mixer.h>
16 #include <SDL_image.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #include "game.h"
21 
22 Uint32 game_timer(Uint32 interval, void *data);
23 void draw(struct game_t *g);
24 static SDL_Surface *load_img(char *fname);
25 
26 SDL_Surface *screen;
27 SDL_Surface *background;
28 SDL_Surface *help;
29 SDL_Surface *blits;
30 
31 enum blitrect {
32 	BR_CURSOR,
33 	BR_PRESS_S_TO_START,
34 
35 	BR_BLOCK1,
36 	BR_BLOCK2,
37 	BR_BLOCK3,
38 	BR_BLOCK4,
39 	BR_BLOCK5,
40 	BR_BLOCK6,
41 
42 	BR_EXPLODING1,
43 	BR_EXPLODING2,
44 	BR_EXPLODING3,
45 	BR_EXPLODING4,
46 	BR_EXPLODING5,
47 
48 	BR_BONUS_1,
49 	BR_BONUS_2,
50 	BR_BONUS_3,
51 
52 	BR_BACKGROUND,
53 	BR_PAUSE,
54 
55 	BR_CHAR_0,
56 	BR_CHAR_1,
57 	BR_CHAR_2,
58 	BR_CHAR_3,
59 	BR_CHAR_4,
60 
61 	BR_CHAR_5,
62 	BR_CHAR_6,
63 	BR_CHAR_7,
64 	BR_CHAR_8,
65 	BR_CHAR_9,
66 
67 	BR_GAME_OVER,
68 };
69 
70 
71 SDL_Rect blitrect[] = {
72 	[BR_CURSOR] =    	{   0,   0,  64, 32 },
73 	[BR_PRESS_S_TO_START]   {  64,   0, 192, 32 },
74 
75 	[BR_BLOCK1] =    	{   0,  32,  32, 32 },
76 	[BR_BLOCK2] =    	{  32,  32,  32, 32 },
77 	[BR_BLOCK3] =    	{  64,  32,  32, 32 },
78 	[BR_BLOCK4] =    	{  96,  32,  32, 32 },
79 	[BR_BLOCK5] =    	{ 128,  32,  32, 32 },
80 	[BR_BLOCK6] =    	{ 160,  32,  32, 32 },
81 
82 	[BR_EXPLODING1] =	{   0,  64,  32, 32 },
83 	[BR_EXPLODING2] =	{  32,  64,  32, 32 },
84 	[BR_EXPLODING3] =	{  64,  64,  32, 32 },
85 	[BR_EXPLODING4] =	{  96,  64,  32, 32 },
86 	[BR_EXPLODING5] =	{ 128,  64,  32, 32 },
87 
88 	[BR_BONUS_1] =		{ 160,  64,  32, 32 },
89 	[BR_BONUS_2] =		{ 192,  64,  32, 32 },
90 	[BR_BONUS_3] =		{ 224,  64,  32, 32 },
91 
92 	[BR_BACKGROUND] =	{   0,  96,  32, 32 },
93 	[BR_PAUSE] =     	{  32,  96, 128, 32 },
94 
95 	[BR_CHAR_0] =    	{   0, 128,  32, 32 },
96 	[BR_CHAR_1] =    	{  32, 128,  32, 32 },
97 	[BR_CHAR_2] =    	{  64, 128,  32, 32 },
98 	[BR_CHAR_3] =    	{  96, 128,  32, 32 },
99 	[BR_CHAR_4] =    	{ 128, 128,  32, 32 },
100 
101 	[BR_CHAR_5] =    	{   0, 160,  32, 32 },
102 	[BR_CHAR_6] =    	{  32, 160,  32, 32 },
103 	[BR_CHAR_7] =    	{  64, 160,  32, 32 },
104 	[BR_CHAR_8] =    	{  96, 160,  32, 32 },
105 	[BR_CHAR_9] =    	{ 128, 160,  32, 32 },
106 
107 	[BR_GAME_OVER] = 	{   0, 192, 192, 64 },
108 };
109 
110 enum sample {
111 	SAMPLE_START,
112 	SAMPLE_BONUS_1,
113 	SAMPLE_NEW_BLOCK,
114 	SAMPLE_FALL,
115 	SAMPLE_SCORE,
116 	SAMPLE_BONUS_2,
117 	SAMPLE_BONUS_3,
118 	SAMPLE_HURRY,
119 	SAMPLE_PAUSE,
120 	SAMPLE_GAME_OVER,
121 	SAMPLE_EARTHQUAKE,
122 
123 	NUM_SAMPLES
124 };
125 
126 struct sample_t {
127 	char *fname;
128 	Mix_Chunk *chunk;
129 };
130 
131 struct sample_t sample_list[NUM_SAMPLES] = {
132 	[SAMPLE_START] = 	{ "/usr/local/share/agame/wav/start.wav" },
133 	[SAMPLE_NEW_BLOCK] = 	{ "/usr/local/share/agame/wav/new_block.wav" },
134 	[SAMPLE_FALL] = 	{ "/usr/local/share/agame/wav/fall.wav" },
135 	[SAMPLE_SCORE] = 	{ "/usr/local/share/agame/wav/score.wav" },
136 	[SAMPLE_BONUS_1] = 	{ "/usr/local/share/agame/wav/explode.wav" },
137 	[SAMPLE_BONUS_2] = 	{ "/usr/local/share/agame/wav/bonus.wav" },
138 	[SAMPLE_BONUS_3] = 	{ "/usr/local/share/agame/wav/bonus2.wav" },
139 	[SAMPLE_HURRY] = 	{ "/usr/local/share/agame/wav/hurry.wav" },
140 	[SAMPLE_PAUSE] = 	{ "/usr/local/share/agame/wav/pause.wav" },
141 	[SAMPLE_GAME_OVER] = 	{ "/usr/local/share/agame/wav/game_over.wav" },
142 	[SAMPLE_EARTHQUAKE] = 	{ "/usr/local/share/agame/wav/earthquake.wav" },
143 };
144 
145 
146 struct floating_score {
147 	int x;
148 	int y;
149 	int dx;
150 	int dy;
151 	int points;
152 	int visible;
153 };
154 
155 static struct floating_score floating_score;
156 static void game_callback(struct game_t *g, struct game_event *event);
157 static int have_audio = 0;
158 
main(int argc,char ** argv)159 int main(int argc, char **argv)
160 {
161 	struct game_t *g;
162 	SDL_Event ev;
163 	SDL_Surface *icon;
164 	int i;
165 	int r;
166 	Mix_Music *music;
167 	int volume;
168 	struct game_action action;
169 
170 	/*
171 	 * Init SDL
172 	 */
173 
174 	SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
175 	atexit(SDL_Quit);
176 	SDL_WM_SetCaption("Game", "Game");
177 
178 	/*
179 	 * Set icon and init video
180 	 */
181 
182 	icon = IMG_Load("/usr/local/share/agame/img/icon.png");
183 	if(icon) {
184 		SDL_WM_SetIcon(icon, NULL);
185 		SDL_FreeSurface(icon);
186 	}
187 
188 	screen = SDL_SetVideoMode(BOARD_W*32+3, BOARD_H*32+3, 0, 0);
189 	SDL_EnableKeyRepeat(100, 40);
190 
191 	blits      = load_img("/usr/local/share/agame/img/game.png");
192 	background = load_img("/usr/local/share/agame/img/background.png");
193 	help       = load_img("/usr/local/share/agame/img/help.png");
194 
195 	/*
196 	 * Init audio
197 	 */
198 
199 	r = Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 1, 1024);
200 	if(r != 0) {
201 		printf("No audio: %s\n", Mix_GetError());
202 	} else {
203 		have_audio = 1;
204 
205 		music = Mix_LoadMUS("/usr/local/share/agame/mp3/track-01.mp3");
206 		if(music) {
207 			Mix_VolumeMusic(64);
208 			Mix_PlayMusic(music, -1);
209 		} else {
210 			printf("Can't load music: %s\n", Mix_GetError());
211 		}
212 
213 		Mix_AllocateChannels(16);
214 		for(i=0; i<NUM_SAMPLES; i++) {
215 			sample_list[i].chunk = Mix_LoadWAV(sample_list[i].fname);
216 			if(sample_list[i].chunk == NULL) {
217 				fprintf(stderr, "Error loading wav: %s\n", Mix_GetError());
218 				exit(1);
219 			}
220 			Mix_VolumeChunk(sample_list[i].chunk, 128);
221 		}
222 	}
223 
224 	/*
225 	 * Register timer
226 	 */
227 
228 	SDL_AddTimer(40, game_timer, NULL);
229 
230 	/*
231 	 * Start game
232 	 */
233 
234 	g = game_new();
235 	if(g == NULL) exit(1);
236 	game_register_callback(g, game_callback);
237 
238 	while(SDL_WaitEvent(&ev)) {
239 
240 		switch(ev.type) {
241 
242 			case SDL_QUIT:
243 				exit(0);
244 				break;
245 
246 			case SDL_MOUSEMOTION:
247 
248 				action.id = GAME_ACTION_SET_CURSOR;
249 				action.data.set_cursor.x = (ev.motion.x-16) / 32;
250 				action.data.set_cursor.y = ev.motion.y / 32;
251 				game_do(g, &action);
252 				break;
253 
254 			case SDL_MOUSEBUTTONDOWN:
255 				action.id = GAME_ACTION_FLIP;
256 				game_do(g, &action);
257 				break;
258 
259 
260 			case SDL_KEYDOWN:
261 
262 				if(ev.key.state == SDL_PRESSED) {
263 
264 					switch(ev.key.keysym.sym) {
265 
266 						case SDLK_ESCAPE:
267 							exit(0);
268 							break;
269 
270 						case 'p':
271 							action.id = GAME_ACTION_PAUSE;
272 							game_do(g, &action);
273 							break;
274 
275 						case SDLK_UP:
276 						case 'k':
277 							action.id = GAME_ACTION_UP;
278 							game_do(g, &action);
279 							break;
280 
281 						case SDLK_DOWN:
282 						case 'j':
283 							action.id = GAME_ACTION_DOWN;
284 							game_do(g, &action);
285 							break;
286 
287 						case SDLK_LEFT:
288 						case 'h':
289 							action.id = GAME_ACTION_LEFT;
290 							game_do(g, &action);
291 							break;
292 
293 						case SDLK_RIGHT:
294 						case 'l':
295 							action.id = GAME_ACTION_RIGHT;
296 							game_do(g, &action);
297 							break;
298 
299 						case SDLK_SPACE:
300 							action.id = GAME_ACTION_FLIP;
301 							game_do(g, &action);
302 							break;
303 
304 						case 's':
305 							action.id = GAME_ACTION_START;
306 							game_do(g, &action);
307 							break;
308 
309 						case 'e':
310 							action.id = GAME_ACTION_EARTHQUAKE;
311 							game_do(g, &action);
312 							break;
313 
314 						case 'm':
315 							Mix_VolumeMusic(0);
316 							break;
317 
318 						case '-':
319 							volume = Mix_VolumeMusic(-1) - 10;
320 							if(volume < 0) volume = 0;
321 							Mix_VolumeMusic(volume);
322 							break;
323 
324 						case '=':
325 							volume = Mix_VolumeMusic(-1) + 10;
326 							if(volume > 128) volume = 128;
327 							Mix_VolumeMusic(volume);
328 							break;
329 
330 
331 						default:
332 							break;
333 					}
334 				}
335 				break;
336 
337 			case SDL_USEREVENT:
338 
339 				action.id = GAME_ACTION_TICK;
340 				game_do(g, &action);
341 				draw(g);
342 				break;
343 
344 			default:
345 				break;
346 
347 		}
348 	}
349 
350 	return 0;
351 }
352 
353 
blit(struct game_t * g,enum blitrect id,int x,int y)354 static void blit(struct game_t *g, enum blitrect id, int x, int y)
355 {
356 	SDL_Rect r;
357 	int delta;
358 	if(g->earthquake_counter) {
359 		delta = g->earthquake_counter / 2;
360 		if(delta < 1) delta = 1;
361 		x += (rand() % delta) - delta/2;
362 		y += (rand() % delta) - delta/2;
363 	}
364 	r.x = x;
365 	r.y = y;
366 	SDL_BlitSurface(blits, &blitrect[id], screen, &r);
367 }
368 
369 
draw(struct game_t * g)370 void draw(struct game_t *g)
371 {
372 	struct cell_t *c;
373 	int x, y;
374 	static int blink_counter = 0;
375 	int blink;
376 	int i;
377 	char score[BOARD_W+1];
378 
379 	if(blink_counter++ == 15) blink_counter = 0;
380 	blink = (blink_counter < 10);
381 
382 	/*
383 	 * Clear screen
384 	 */
385 
386 	SDL_FillRect(screen, NULL, 0);
387 	SDL_BlitSurface(background, NULL, screen, NULL);
388 
389 	/*
390 	 * Draw cells
391 	 */
392 
393 	for(y=0; y<BOARD_H; y++) {
394 		for(x=0; x<BOARD_W; x++) {
395 
396 			c = &g->cell[x][y];
397 
398 			if((c->contents > 0) && (c->contents <= g->num_blocks) && !(g->state == GAME_STATE_PAUSE)) {
399 				blit(g, c->contents -  1 + BR_BLOCK1, x*32, y*32);
400 				if(c->exploding) blit(g, c->exploding - 1 + BR_EXPLODING1, x*32, y*32);
401 
402 			} else {
403 				blit(g, BR_BACKGROUND, x*32, y*32);
404 			}
405 
406 
407 		}
408 	}
409 
410 	/*
411 	 * Draw cursor
412 	 */
413 
414 	for(i=0; i<blink+1; i++) blit(g, BR_CURSOR, g->cursor_x * 32, g->cursor_y * 32);
415 
416 	/*
417 	 * Draw score
418 	 */
419 
420 	snprintf(score, sizeof(score), "%*d", BOARD_W, g->score_counter);
421 	for(x=0; x<strlen(score); x++) {
422 		if(score[x] != ' ') blit(g, BR_CHAR_0 + score[x] - '0', x*32, 0);
423 	}
424 
425 	if(g->state == GAME_STATE_IDLE) {
426 		blit(g, BR_PRESS_S_TO_START, 0, 32*6);
427 		SDL_BlitSurface(help, NULL, screen, NULL);
428 	}
429 
430 	if(g->state == GAME_STATE_GAME_OVER) {
431 		blit(g, BR_GAME_OVER, 0, 32*4);
432 		blit(g, BR_PRESS_S_TO_START, 0, 32*6);
433 	}
434 
435 	if(g->state == GAME_STATE_PAUSE) {
436 		blit(g, BR_PAUSE, 32*1, 32*4);
437 		if(have_audio) {
438 			if(! Mix_Playing(15)) Mix_PlayChannel(15, sample_list[SAMPLE_PAUSE].chunk, 0);
439 		}
440 	}
441 
442 	/*
443 	 * Draw floating score
444 	 */
445 
446 	if(floating_score.visible) {
447 		if(floating_score.points ==  5) blit(g, BR_BONUS_1, floating_score.x, floating_score.y);
448 		if(floating_score.points == 10) blit(g, BR_BONUS_2, floating_score.x, floating_score.y);
449 		if(floating_score.points == 15) blit(g, BR_BONUS_3, floating_score.x, floating_score.y);
450 		floating_score.x += floating_score.dx;
451 		floating_score.y += floating_score.dy;
452 
453 		if((floating_score.x < 0) || (floating_score.y <0)) {
454 			floating_score.visible = 0;
455 		}
456 	}
457 
458 
459 	SDL_UpdateRect(screen, 0, 0, 0, 0);
460 
461 }
462 
463 
game_timer(Uint32 interval,void * data)464 Uint32 game_timer(Uint32 interval, void *data)
465 {
466 	SDL_Event ev;
467 
468 	ev.user.type = SDL_USEREVENT;
469 	ev.user.code = 0;
470 	SDL_PushEvent(&ev);
471 
472 	return interval;
473 }
474 
game_callback(struct game_t * g,struct game_event * event)475 static void game_callback(struct game_t *g, struct game_event *event)
476 {
477 	struct sample_t *sample = NULL;
478 
479 	switch(event->id) {
480 
481 		case GAME_EVENT_START:
482 			sample = &sample_list[SAMPLE_START];
483 			break;
484 
485 		case GAME_EVENT_EXPLODING :
486 			if(event->data.exploding.blocks == 3) sample = &sample_list[SAMPLE_BONUS_1];
487 			if(event->data.exploding.blocks == 4) sample = &sample_list[SAMPLE_BONUS_2];
488 			if(event->data.exploding.blocks == 5) sample = &sample_list[SAMPLE_BONUS_3];
489 			if(event->data.exploding.blocks == 6) sample = &sample_list[SAMPLE_BONUS_3];
490 
491 			floating_score.x = event->data.exploding.x * 32;
492 			floating_score.y = event->data.exploding.y * 32;
493 			floating_score.points = event->data.exploding.points;
494 			floating_score.dx = -(floating_score.x - BOARD_W*32/2) / 10;
495 			floating_score.dy = -(rand() % 16) - 12;
496 			floating_score.visible = 1;
497 
498 			break;
499 
500 		case GAME_EVENT_NEW_BLOCK:
501 			sample = &sample_list[SAMPLE_NEW_BLOCK];
502 			break;
503 
504 		case GAME_EVENT_FALL:
505 			sample = &sample_list[SAMPLE_FALL];
506 			break;
507 
508 		case GAME_EVENT_SCORE_UPDATE:
509 			sample = &sample_list[SAMPLE_SCORE];
510 			break;
511 
512 		case GAME_EVENT_HURRY:
513 			sample = &sample_list[SAMPLE_HURRY];
514 			break;
515 
516 		case GAME_EVENT_GAME_OVER:
517 			sample = &sample_list[SAMPLE_GAME_OVER];
518 			break;
519 
520 		case GAME_EVENT_EARTHQUAKE:
521 			sample = &sample_list[SAMPLE_EARTHQUAKE];
522 			break;
523 
524 	};
525 
526 	if(have_audio && sample) Mix_PlayChannel(-1, sample->chunk, 0);
527 }
528 
529 
530 /*
531  * Load image and convert to display format
532  */
533 
load_img(char * fname)534 static SDL_Surface *load_img(char *fname)
535 {
536 	SDL_Surface *surf1, *surf2;
537 
538 	surf1 = IMG_Load(fname);
539 	if(surf1 == NULL) {
540 		fprintf(stderr, "Can't load image '%s': %s\n", fname, SDL_GetError());
541 		exit(1);
542 	}
543 
544 	surf2 = SDL_DisplayFormatAlpha(surf1);
545 	if(surf2 == NULL) {
546 		fprintf(stderr, "Can't convert image to display format: %s\n", SDL_GetError());
547 		exit(1);
548 	}
549 
550 	SDL_FreeSurface(surf1);
551 
552 	return surf2;
553 }
554 
555 /*
556  * End
557  */
558 
559