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