1 /*
2 gemdropx.c
3
4 Gem Drop for SDL (X-Window, MSWindows, MacOS, BeOS, etc.) and Qt/Embedded
5
6 v.0.9
7
8 based directly on GEMDROP.ACT, the original Atari 8-bit version of Gem Drop
9
10 by Bill Kendrick
11 gemdropx@newbreedsoftware.com
12 http://www.newbreedsoftware.com/gemdropx/
13
14 Atari version: August 17, 1997 - Sept. 24, 1997
15 X Window version: November 5, 1997 - November 21, 1997
16 X Window update: July 29, 1998 - April 4, 1999
17 SDL mixer fixes: October 31, 1999
18 100% SDL version: December 27, 1999
19 SDL fixes: December 29, 1999 - January 26, 2000
20 Sound tweaks: February 11, 2002
21 Qt/Embedded Support: February 11, 2002 - February 12, 2002
22 */
23
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <math.h>
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32
33 #ifndef EMBEDDED
34 #include <SDL.h>
35 #else
36 #include <qtimer.h>
37 #include <qpainter.h>
38 #include <qpe/qpeapplication.h>
39 #include <qwidget.h>
40 #include <qobject.h>
41 #endif
42
43 #ifndef NOSOUND
44 #include <SDL_mixer.h>
45 #endif
46
47 #ifndef EMBEDDED
48 #include "data/images/nothing.xbm"
49 #include "data/images/nothing-mask.xbm"
50 #include "data/images/left.xbm"
51 #include "data/images/left-mask.xbm"
52 #include "data/images/right.xbm"
53 #include "data/images/right-mask.xbm"
54 #include "data/images/action.xbm"
55 #include "data/images/action-mask.xbm"
56 #endif
57
58
59 /* Misc. value defines: */
60
61 #define NO 0
62 #define YES 1
63
64
65 /* Game stuff: */
66
67 #define FPS 20
68 #define HEIGHT 11
69 #define WIDTH 10
70 #define NUM_EXPLOSIONS 100
71 #define STICKWAIT 2
72
73
74 /* Explosion type: */
75
76 typedef struct explosion_type {
77 int exist; /* do I exist? */
78 int x, y; /* position */
79 int anim; /* animation frame counter */
80 int animrate; /* pause for animation */
81 } explosion_type;
82
83
84 #ifdef EMBEDDED
85 typedef struct SDL_Rect { int x, y, w, h; } SDL_Rect;
86 typedef struct SDL_Surface { int w, h; QPixmap * image; } SDL_Surface;
87 #define Uint8 unsigned char
88 #define Uint32 unsigned long
89 #endif
90
91
92 /* Object specifications: */
93
94 #define NUM_NORMAL_BLOCKS 4
95 #define NUM_ALL_BLOCKS 8
96 #define BOMB NUM_ALL_BLOCKS + 1
97 #define CLOCK NUM_ALL_BLOCKS + 2
98 #define WILDCARD NUM_ALL_BLOCKS + 3
99 #define NUM_SPECIALS 3
100 #define NUM_EXPLOSION_GFX 4
101 #define EXPLOSION_SLOW_VAL 1
102 #define HAPPY_GFX NUM_ALL_BLOCKS + NUM_SPECIALS + NUM_EXPLOSION_GFX + 1
103 #define SAD_GFX HAPPY_GFX + 1
104 #define WIN1_GFX SAD_GFX + 1
105 #define WIN2_GFX WIN1_GFX + 1
106 #define PATIENCE_GFX WIN2_GFX + 1
107 #define WARNING_GFX PATIENCE_GFX + 1
108 #define BRICK_GFX WARNING_GFX + 4
109 #define TITLE_GFX BRICK_GFX + 1
110 #define CREDITS_GFX TITLE_GFX + 1
111 #define LEVEL_GFX CREDITS_GFX + 1
112 #define LINES_GFX LEVEL_GFX + 1
113 #define NEED_GFX LINES_GFX + 1
114 #define NUMBERS_GFX NEED_GFX + 1
115 #define PAUSED_GFX NUMBERS_GFX + 1
116 #define SCORE_GFX PAUSED_GFX + 1
117 #define AUTHOR_GFX SCORE_GFX + 1
118 #define UHOH_GFX AUTHOR_GFX + 1
119 #define CONGRATULATIONS_GFX UHOH_GFX + 1
120 #define NUM_OBJECTS CONGRATULATIONS_GFX
121
122
123 /* Object (graphics) filenames: */
124
125 char * object_filenames[NUM_OBJECTS] = {"block", "yellowgem",
126 "diamond", "disc",
127 "dot", "bluegem", "fuji", "triangle",
128 "bomb", "stopwatch", "wildcard",
129 "explode0", "explode1",
130 "explode2", "explode3",
131 "happy", "sad", "win1", "win2",
132 "patience", "warning1", "warning2",
133 "warning3", "warning4", "brick",
134 "title", "credits", "level", "lines",
135 "need", "numbers", "paused", "score",
136 "author", "uhoh", "congratulations"};
137
138
139 /* Sounds: */
140
141 enum {
142 SND_BEGIN,
143 SND_BOMB,
144 SND_CLOCK,
145 SND_GAMEOVER,
146 SND_GOT,
147 SND_LEVELSELECT,
148 SND_WILDCARD,
149 SND_WIN,
150 SND_GRAB,
151 SND_THROW,
152 SND_BUZZER,
153 SND_APPLAUSE,
154 NUM_SOUNDS
155 };
156
157 /* Sound filenames: */
158
159 char * sound_filenames[NUM_SOUNDS] = {
160 "begin", "bomb", "clock", "gameover", "got", "levelselect",
161 "wildcard", "win", "grab", "throw", "buzzer", "applause"
162 };
163
164 enum {
165 MOD_TITLE,
166 MOD_GAME1,
167 MOD_GAME2,
168 MOD_GAME3,
169 NUM_MODS
170 };
171
172 #define NUM_GAME_MODS 3
173
174 char * music_filenames[NUM_MODS] = {
175 "hr_suds.xm", "22drops.mod", "citron.mod", "2force.xm"
176 };
177
178 #ifndef NOSOUND
179 Mix_Chunk * samples[NUM_SOUNDS];
180 Mix_Music * songs[NUM_MODS];
181 #endif
182
183
184 #ifndef EMBEDDED
185
186 /* Cursors: */
187
188 enum {
189 CURSOR_NOTHING,
190 CURSOR_LEFT,
191 CURSOR_RIGHT,
192 CURSOR_ACTION,
193 NUM_CURSORS
194 };
195
196 Uint8 * cursor_bitmap[NUM_CURSORS] = {
197 nothing_bits, left_bits, right_bits, action_bits
198 };
199
200 Uint8 * cursor_bitmask[NUM_CURSORS] = {
201 nothing_mask_bits, left_mask_bits, right_mask_bits, action_mask_bits
202 };
203
204 #endif
205
206
207 #ifdef EMBEDDED
208 #define SDLK_ESCAPE 27
209 #define SDLK_a 'a'
210 #define SDLK_k 'k'
211 #define SDLK_l 'l'
212 #define SDLK_p 'p'
213 #define SDLK_q 'q'
214 #define SDLK_z 'z'
215 #define SDLK_RETURN 10
216 #define SDLK_SPACE 32
217 #define SDLK_LAST 999
218 #define SDLK_RIGHT '<'
219 #define SDLK_LEFT '>'
220 #define SDLK_UP '^'
221 #define SDLK_DOWN 'v'
222 #endif
223
224
225 /* Globals: */
226
227 explosion_type explosions[NUM_EXPLOSIONS];
228 int blocks[HEIGHT][WIDTH], killsx[100], killsy[100];
229 int scorevals[15];
230 int level, gameover, frozen, carrying, howmany, leveldone, _warning,
231 whichexplosion, lines, firstround, linesneeded, happy, score, playerx,
232 use_sound, use_joystick, max_level, clicks, no_music, game_playing;
233
234
235 int windowwidth, windowheight;
236
237
238 SDL_Surface * window;
239 SDL_Surface * object_pixmaps[NUM_OBJECTS];
240
241 #ifndef EMBEDDED
242 SDL_Cursor * cursor[NUM_CURSORS];
243 SDL_Event event;
244 SDLKey key;
245 SDL_Joystick * js;
246 #else
247
248 class MainWindow: public QWidget
249 {
250 // Q_OBJECT
251 private:
252 QPixmap* buffer;
253 QPainter* bufpainter;
254 QColor palette[2];
255
256 int (*timerhandler)();
257 int (*keypresshandler)(int);
258 int (*keyreleasehandler)(int);
259
260 protected:
261 void timerEvent(QTimerEvent*);
262 void keyPressEvent(QKeyEvent*);
263 void keyReleaseEvent(QKeyEvent*);
264
265 public:
266 MainWindow(const char* name);
267 void setTimerhandler(int (*timerhandler)(void), int ms = 15);
268 void setKeypresshandler(int (*keypresshandler)(int));
269 void setKeyreleasehandler(int (*keyreleasehandler)(int));
270
271 void setBgColor(Q_UINT32 color);
272 void setFgColor(Q_UINT32 color);
273
274 void plot(int x, int y);
275 void flush();
276
277 void blit(QPixmap* image, int x, int y);
278 };
279
MainWindow(const char * name)280 MainWindow::MainWindow(const char* name):
281 QWidget(0, name, WType_TopLevel)
282 {
283 // Setup the buffer
284 buffer = new QPixmap(240, 320);
285 bufpainter = new QPainter(buffer);
286
287 // Initialize variables
288 setBgColor(0x000000);
289 setFgColor(0xffffff);
290
291 // Initialize handlers
292 timerhandler = NULL;
293 keypresshandler = NULL;
294 keyreleasehandler = NULL;
295
296 // Setup the window
297 resize(230, 280);
298 setCaption(name);
299 bufpainter->eraseRect(0, 0, 240, 320);
300 }
301
setTimerhandler(int (* th)(void),int ms)302 void MainWindow::setTimerhandler(int (*th)(void), int ms)
303 {
304 timerhandler = th; // Install the timer handler
305 startTimer(ms);
306 }
307
setKeypresshandler(int (* kph)(int))308 void MainWindow::setKeypresshandler(int (*kph)(int))
309 {
310 keypresshandler = kph; // Install the keypress handler
311 }
312
setKeyreleasehandler(int (* krh)(int))313 void MainWindow::setKeyreleasehandler(int (*krh)(int))
314 {
315 keyreleasehandler = krh; // Install the keypress handler
316 }
317
timerEvent(QTimerEvent *)318 void MainWindow::timerEvent(QTimerEvent*)
319 {
320 timerhandler();
321 }
322
323
keyPressEvent(QKeyEvent * e)324 void MainWindow::keyPressEvent(QKeyEvent* e)
325 {
326 if(!e->isAutoRepeat() && keypresshandler)
327 {
328 keypresshandler(e->key());
329 }
330 }
331
332
keyReleaseEvent(QKeyEvent * e)333 void MainWindow::keyReleaseEvent(QKeyEvent* e)
334 {
335 if(!e->isAutoRepeat() && keyreleasehandler)
336 {
337 keyreleasehandler(e->key());
338 }
339 }
340
341
setBgColor(Q_UINT32 color)342 void MainWindow::setBgColor(Q_UINT32 color)
343 {
344 palette[0] = QColor((color >> 16 & 0xff), (color >> 8) & 0xff, color & 0xff);
345 bufpainter->setBackgroundColor(palette[0]);
346 }
347
348
setFgColor(Q_UINT32 color)349 void MainWindow::setFgColor(Q_UINT32 color)
350 {
351 palette[1] = QColor((color >> 16 & 0xff), (color >> 8) & 0xff, color & 0xff);
352 bufpainter->setPen(palette[1]);
353 }
354
355
plot(int x,int y)356 void MainWindow::plot(int x, int y)
357 {
358 bufpainter->drawPoint(x, y);
359 }
360
361
flush()362 void MainWindow::flush()
363 {
364 QPainter p(this);
365 p.drawPixmap(0, 0, *buffer);
366 }
367
368
blit(QPixmap * image,int x,int y)369 void MainWindow::blit(QPixmap* image, int x, int y)
370 {
371 bufpainter->drawPixmap(x, y, *image);
372 }
373
374
375
376 QPEApplication * qapp;
377 MainWindow * mw;
378 int key;
379 #endif
380
381
382 #ifdef EMBEDDED
SDL_BlitSurface(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)383 int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect,
384 SDL_Surface *dst, SDL_Rect *dstrect)
385 {
386 mw->blit(src->image, dstrect->x, dstrect->y);
387 return(0);
388 }
389
SDL_UpdateRect(SDL_Surface * screen,int x,int y,int w,int h)390 int SDL_UpdateRect(SDL_Surface *screen, int x, int y, int w, int h)
391 {
392 printf("$$ SDL_UpdateRect()\n");
393 return(0);
394 }
395
SDL_GetTicks(void)396 Uint32 SDL_GetTicks(void)
397 {
398 struct timeval tv;
399
400 gettimeofday(&tv, NULL);
401 return (tv.tv_sec * 100000 + tv.tv_usec) / 1000;
402 }
403
SDL_Delay(Uint32 ms)404 void SDL_Delay(Uint32 ms)
405 {
406 usleep(ms * 1000);
407 }
408
SDL_LoadBMP(const char * file)409 SDL_Surface * SDL_LoadBMP(const char * file)
410 {
411 SDL_Surface * surf;
412
413 surf = (SDL_Surface *) malloc(sizeof(SDL_Surface));
414
415 surf->image = new QPixmap();
416 surf->image->load(file);
417 surf->w = (surf->image)->width();
418 surf->h = (surf->image)->height();
419 return(surf);
420 }
421
SDL_GetError(void)422 char * SDL_GetError(void)
423 {
424 printf("$$ SDL_GetError()\n");
425 return ("???");
426 }
427
SDL_Quit(void)428 void SDL_Quit(void)
429 {
430 exit(1);
431 }
432 #endif
433
434
my_FillRect(SDL_Surface * dst,SDL_Rect * dstrect,Uint8 r,Uint8 g,Uint8 b)435 int my_FillRect(SDL_Surface *dst, SDL_Rect *dstrect,
436 Uint8 r, Uint8 g, Uint8 b)
437 {
438 #ifndef EMBEDDED
439 return (SDL_FillRect(dst, dstrect,
440 SDL_MapRGB(dst->format, r, g, b)));
441 #else
442 printf("$$ my_FillRect()\n");
443 return(0);
444 #endif
445 }
446
447
playsound(int which)448 void playsound(int which)
449 {
450 #ifndef NOSOUND
451 int i, found;
452
453 if (use_sound)
454 {
455 found = -1;
456
457 for (i = 0; i < MIX_CHANNELS; i++)
458 {
459 if (!Mix_Playing(i))
460 found = i;
461 }
462
463 if (found == -1)
464 found = rand() % MIX_CHANNELS;
465
466 Mix_PlayChannel(found, samples[which], 0);
467 /* Mix_Volume(found, 8); */
468 }
469 #endif
470 }
471
472
473 /* Beep: (was flash and beep on the Atari) */
474
complain(void)475 void complain(void)
476 {
477 playsound(SND_BUZZER);
478 }
479
480
481 /* Clear the screen: */
482
erasewindow(void)483 void erasewindow(void)
484 {
485 SDL_Rect dest;
486
487 dest.x = 0;
488 dest.y = 0;
489 dest.w = windowwidth;
490 dest.h = windowheight;
491
492 my_FillRect(window, &dest, 0x00, 0x00, 0x00);
493 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
494 }
495
496
497 /* Setup the application: */
498
setup(int argc,char * argv[])499 void setup(int argc, char * argv[])
500 {
501 int i;
502 char file[128];
503 #ifndef EMBEDDED
504 int j;
505 Uint8 temp_bitmap[32], temp_bitmask[32];
506 Uint8 b;
507 SDL_Surface * image;
508 #endif
509 SDL_Rect dest;
510
511
512 #ifndef EMBEDDED
513 if (SDL_Init(SDL_INIT_VIDEO) < 0)
514 {
515 fprintf(stderr, "Couldn't initialize SDL video.\n%s\n",
516 SDL_GetError());
517 exit(1);
518 }
519
520 use_joystick = 1;
521 if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
522 {
523 fprintf(stderr, "Warning - Couldn't initialize SDL joystick.\n%s\n",
524 SDL_GetError());
525 use_joystick = 0;
526 }
527
528
529 /* Set key repeat: */
530
531 SDL_EnableKeyRepeat(100, 30);
532
533
534 /* Open joystick: */
535
536 if (use_joystick)
537 {
538 if (SDL_NumJoysticks() <= 0)
539 {
540 fprintf(stderr, "Warning - No joysticks.\n");
541 use_joystick = 0;
542 }
543 }
544
545 if (use_joystick)
546 {
547 js = SDL_JoystickOpen(0);
548 if (js == NULL)
549 {
550 fprintf(stderr, "Warning - Can't open joystick 1.\n%s\n",
551 SDL_GetError());
552 use_joystick = 0;
553 }
554 }
555
556
557 /* Check for proper joystick config: */
558
559 if (use_joystick)
560 {
561 if (SDL_JoystickNumAxes(js) < 1)
562 {
563 fprintf(stderr, "Joystick doesn't have enough axes!\n");
564 use_joystick = 0;
565 }
566 }
567
568 if (use_joystick)
569 {
570 if (SDL_JoystickNumButtons(js) < 2)
571 {
572 fprintf(stderr, "Joystick doesn't have enough buttons!\n");
573 use_joystick = 0;
574 }
575 }
576 #else
577 qapp = new QPEApplication(argc, argv);
578 mw = new MainWindow("Gem Drop Z");
579 #endif
580
581
582 /* Set the size of the window: */
583
584 #ifndef EMBEDDED
585 windowwidth = WIDTH * 48;
586 windowheight = (HEIGHT + 2) * 48;
587 #else
588 windowwidth = 240;
589 windowheight = 320;
590 #endif
591
592
593 /* Open window: */
594
595 #ifndef EMBEDDED
596 window = SDL_SetVideoMode(windowwidth, windowheight, 16, SDL_HWSURFACE);
597 SDL_WM_SetCaption("Gem Drop X!", "Gem Drop X");
598 #else
599 printf("$$ setup() - open window\n");
600 #endif
601
602
603 #ifndef EMBEDDED
604 /* Make cursors: */
605
606 for (i = 0; i < NUM_CURSORS; i++)
607 {
608 for (j = 0; j < 32; j++)
609 {
610 b = cursor_bitmap[i][j];
611
612 temp_bitmap[j] = (((b & 0x01) << 7) |
613 ((b & 0x02) << 5) |
614 ((b & 0x04) << 3) |
615 ((b & 0x08) << 1) |
616 ((b & 0x10) >> 1) |
617 ((b & 0x20) >> 3) |
618 ((b & 0x40) >> 5) |
619 ((b & 0x80) >> 7));
620
621 b = cursor_bitmask[i][j];
622
623 temp_bitmask[j] = (((b & 0x01) << 7) |
624 ((b & 0x02) << 5) |
625 ((b & 0x04) << 3) |
626 ((b & 0x08) << 1) |
627 ((b & 0x10) >> 1) |
628 ((b & 0x20) >> 3) |
629 ((b & 0x40) >> 5) |
630 ((b & 0x80) >> 7));
631 }
632
633 cursor[i] = SDL_CreateCursor(temp_bitmap, temp_bitmask,
634 16, 16, 8, 8);
635 }
636 #endif
637
638
639 /* Load the object graphics: */
640
641 for (i = 0; i < NUM_OBJECTS; i++)
642 {
643 sprintf(file, "%s/images/%s.bmp", DATA_PREFIX, object_filenames[i]);
644
645 #ifndef EMBEDDED
646 image = SDL_LoadBMP(file);
647
648 if (image == NULL)
649 {
650 fprintf(stderr, "%s: can't load: %s\n", file, SDL_GetError());
651 SDL_Quit();
652 exit(1);
653 }
654
655 object_pixmaps[i] = SDL_DisplayFormat(image);
656 SDL_FreeSurface(image);
657 #else
658 object_pixmaps[i] = SDL_LoadBMP(file);
659
660 if (object_pixmaps[i] == NULL)
661 {
662 fprintf(stderr, "%s: can't load: %s\n", file, SDL_GetError());
663 SDL_Quit();
664 exit(1);
665 }
666 #endif
667
668
669
670 if (i == PATIENCE_GFX)
671 {
672 dest.x = (windowwidth - 96) / 2;
673 dest.y = (windowheight - 16) / 2;
674 dest.w = 96;
675 dest.h = 16;
676
677 SDL_BlitSurface(object_pixmaps[PATIENCE_GFX - 1], NULL,
678 window, &dest);
679 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
680 }
681 }
682
683
684 /* Initial level: */
685
686 level = 1;
687
688
689 /* Reset explosions: */
690
691 for (i = 0; i < NUM_EXPLOSIONS; i++)
692 {
693 explosions[i].exist = NO;
694 }
695
696
697 /* Init. score value table: */
698
699 scorevals[0] = 0;
700 scorevals[1] = 0;
701 scorevals[2] = 0;
702 scorevals[3] = 10;
703 scorevals[4] = 20;
704 scorevals[5] = 50;
705 scorevals[6] = 100;
706 scorevals[7] = 150;
707 scorevals[8] = 200;
708 scorevals[9] = 250;
709 scorevals[10] = 300;
710 scorevals[11] = 500;
711 scorevals[12] = 1000;
712 scorevals[13] = 1000;
713 scorevals[14] = 5000;
714
715
716 /* Reset random number generator: */
717
718 srand(SDL_GetTicks());
719
720
721 /* Setup sound: */
722
723 use_sound = 1;
724
725 #ifndef NOSOUND
726 if (use_sound)
727 {
728 if (SDL_Init(SDL_INIT_AUDIO) < 0)
729 {
730 fprintf(stderr, "Warning: Couldn't initialize SDL sound.\n%s\n",
731 SDL_GetError());
732 use_sound = 0;
733 }
734 }
735
736 if (use_sound)
737 {
738 if (Mix_OpenAudio(44100, AUDIO_S8, 2, 512) < 0)
739 {
740 fprintf(stderr,
741 "Warning: Couldn't set 11025 Hz 8-bit stereo audio.\n%s\n",
742 SDL_GetError());
743 use_sound = 0;
744 }
745 }
746
747
748 if (use_sound)
749 {
750 /* Load sound files: */
751
752 for (i = 0; i < NUM_SOUNDS; i++)
753 {
754 sprintf(file, "%s/sounds/%s.wav", DATA_PREFIX, sound_filenames[i]);
755 samples[i] = Mix_LoadWAV(file);
756 if (samples[i] == NULL)
757 {
758 fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError());
759 SDL_Quit();
760 exit(1);
761 }
762 }
763
764
765 /* Load music files: */
766
767 if (no_music == 0)
768 {
769 for (i = 0; i < NUM_MODS; i++)
770 {
771 sprintf(file, "%s/sounds/%s", DATA_PREFIX, music_filenames[i]);
772 songs[i] = Mix_LoadMUS(file);
773 if (songs[i] == NULL)
774 {
775 fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError());
776 SDL_Quit();
777 exit(1);
778 }
779 }
780 }
781 }
782 #endif
783 }
784
785
786 /* Display a number using images: */
787
shownumber(int num,int x,int y)788 void shownumber(int num, int x, int y)
789 {
790 int i;
791 char temp[15];
792 SDL_Rect src, dest;
793
794 num = (num % 100000);
795
796 sprintf(temp, "%d ", num);
797
798 for (i = 0; i < 5; i++)
799 {
800 dest.x = x + i * 16;
801 dest.y = y;
802 dest.w = 16;
803 dest.h = 16;
804
805 if (temp[i] >= '0' && temp[i] <= '9')
806 {
807 src.x = (temp[i] - '0') * 16;
808 src.y = 0;
809 src.w = 16;
810 src.h = 16;
811
812 SDL_BlitSurface(object_pixmaps[NUMBERS_GFX - 1], &src,
813 window, &dest);
814 }
815 else
816 {
817 my_FillRect(window, &dest, 0x00, 0x00, 0x00);
818 }
819
820 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
821 }
822 }
823
824
825 /* Update the score in the window: */
826
drawscore()827 void drawscore()
828 {
829 SDL_Rect dest;
830
831 dest.x = 0;
832 dest.y = (HEIGHT + 2) * 48 - 16;
833 dest.w = 48;
834 dest.h =16;
835
836 SDL_BlitSurface(object_pixmaps[SCORE_GFX - 1], NULL,
837 window, &dest);
838 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
839
840 shownumber(score, 48, (HEIGHT + 2) * 48 - 16);
841 }
842
843
844 /* Pick a random block: */
845
randblock(void)846 int randblock(void)
847 {
848 int i;
849
850
851 /* Pick one of the main four pieces in level 1-14: */
852
853 i = (rand() % NUM_NORMAL_BLOCKS) + 1;
854
855
856 /* Pick one of all eight pieces in levels 15-20: */
857
858 if (level >= 15)
859 i = rand() % (NUM_ALL_BLOCKS + 1);
860
861
862 /* Maybe pick a non-gem object: */
863
864 if ((rand() % 40) < 1)
865 i = (rand() % NUM_SPECIALS) + NUM_ALL_BLOCKS + 1;
866
867 return(i);
868 }
869
870
871 /* Draw an object graphic somewhere in the window: */
872
drawblockgraphic(int x,int y,int c)873 void drawblockgraphic(int x, int y, int c)
874 {
875 SDL_Rect dest;
876
877 dest.x = x;
878 dest.y = y;
879 dest.w = 48;
880 dest.h = 48;
881
882 if (c != 0)
883 {
884 SDL_BlitSurface(object_pixmaps[c - 1], NULL,
885 window, &dest);
886 }
887 else
888 {
889 my_FillRect(window, &dest, 0x00, 0x00, 0x00);
890 }
891
892 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
893 }
894
895
896 /* Update the status icons at the bottom: */
897
drawstatus()898 void drawstatus()
899 {
900 if (frozen != 0)
901 drawblockgraphic((WIDTH / 2) * 48, (HEIGHT + 1) * 48, CLOCK);
902 else
903 drawblockgraphic((WIDTH / 2) * 48, (HEIGHT + 1) * 48, 0);
904
905 if (_warning != 0)
906 drawblockgraphic((WIDTH / 2) * 48 + 48, (HEIGHT + 1) * 48, WARNING_GFX +
907 (clicks % 4));
908 else
909 drawblockgraphic((WIDTH / 2) * 48 + 48, (HEIGHT + 1) * 48, 0);
910 }
911
912
913 /* Draw the player (it's two colors): */
914
drawmangraphic(int x,int y,int c)915 void drawmangraphic(int x, int y, int c)
916 {
917 SDL_Rect dest;
918
919 if (c != 0)
920 {
921 dest.x = x;
922 dest.y = y;
923 dest.w = 48;
924 dest.h = 48;
925
926 SDL_BlitSurface(object_pixmaps[c - 1], NULL,
927 window, &dest);
928 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
929 }
930 }
931
932
933 /* Draw a block where it belongs: */
934
drawblock(int x,int y)935 void drawblock(int x, int y)
936 {
937 int c;
938
939
940 /* Draw the block at (x, y): */
941
942 c = blocks[y][x];
943
944 drawblockgraphic(x * 48, y * 48, c);
945 }
946
947
948 /* Update the level number display: */
949
updatelevel(void)950 void updatelevel(void)
951 {
952 SDL_Rect dest;
953
954 dest.x = 0;
955 dest.y = (HEIGHT + 2) * 48 - 16 - 16;
956 dest.w = 48;
957 dest.h = 16;
958
959 SDL_BlitSurface(object_pixmaps[LEVEL_GFX - 1], NULL,
960 window, &dest);
961 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
962
963 shownumber(level, 48, (HEIGHT + 2) * 48 - 16 - 16);
964 }
965
966
967 /* Update the lines / needed lines displays: */
968
updatelines(void)969 void updatelines(void)
970 {
971 SDL_Rect dest;
972
973 dest.x = WIDTH * 48 - 48 - (16 * 5);
974 dest.y = (HEIGHT + 2) * 48 - 16 - 16;
975 dest.w = 48;
976 dest.h = 16;
977
978 SDL_BlitSurface(object_pixmaps[LINES_GFX - 1], NULL,
979 window, &dest);
980
981 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
982
983 shownumber(lines, WIDTH * 48 - (16 * 5), (HEIGHT + 2) * 48 - 16 - 16);
984
985
986 dest.x = WIDTH * 48 - 48 - (16 * 5);
987 dest.y = (HEIGHT + 2) * 48 - 16;
988 dest.w = 48;
989 dest.h = 16;
990
991 SDL_BlitSurface(object_pixmaps[NEED_GFX - 1], NULL,
992 window, &dest);
993
994 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
995
996 shownumber(linesneeded, WIDTH * 48 - (16 * 5), (HEIGHT + 2) * 48 - 16);
997 }
998
999
1000 /* Get a key... mouse is seen as key, too!
1001 Maybe handle expose events
1002 (the original source that this was ported from was obviously
1003 not written for an event-based system): */
1004
getkey(int handle_expose)1005 void getkey(int handle_expose)
1006 {
1007 /* int x, y; */
1008
1009
1010 #ifndef EMBEDDED
1011 key = SDLK_LAST;
1012
1013 while (SDL_PollEvent(&event))
1014 {
1015 if (event.type == SDL_KEYDOWN)
1016 {
1017 /* Get the key's name: */
1018
1019 key = event.key.keysym.sym;
1020 }
1021 else if (event.type == SDL_MOUSEBUTTONDOWN)
1022 {
1023 /* Mouse counts as keyboard: */
1024
1025 if (event.button.y >= HEIGHT * 48)
1026 {
1027 /* Left or right of man at the bottom counts as left/right: */
1028
1029 if (event.button.x < playerx * 48 + 16)
1030 key = SDLK_LEFT;
1031 else if (event.button.x >= playerx * 48 + 31)
1032 key = SDLK_RIGHT;
1033 }
1034 else
1035 {
1036 /* Left or right click above man count as grab/throw: */
1037
1038 if (event.button.button == 1)
1039 key = SDLK_DOWN;
1040 else
1041 key = SDLK_UP;
1042 }
1043 }
1044 else if (event.type == SDL_MOUSEMOTION)
1045 {
1046 /* Handle cursor shape: */
1047
1048 if (game_playing == 0)
1049 SDL_SetCursor(cursor[CURSOR_NOTHING]);
1050 else
1051 {
1052 if (event.motion.y >= HEIGHT * 48)
1053 {
1054 /* Left/right arrows or nothing: */
1055
1056 if (event.motion.x < playerx * 48 + 16)
1057 SDL_SetCursor(cursor[CURSOR_LEFT]);
1058 else if (event.motion.x >= playerx * 48 + 31)
1059 SDL_SetCursor(cursor[CURSOR_RIGHT]);
1060 else
1061 SDL_SetCursor(cursor[CURSOR_NOTHING]);
1062 }
1063 else
1064 SDL_SetCursor(cursor[CURSOR_ACTION]);
1065 }
1066 }
1067 }
1068 #else
1069 printf("$$ getkey()\n");
1070 #endif
1071 }
1072
1073
1074 /* Wait until joystick buttons are released: */
1075
1076 #ifndef EMBEDDED
1077
eatjoybuttons(void)1078 void eatjoybuttons(void)
1079 {
1080 int i, any_down;
1081 SDL_Event event;
1082
1083
1084 if (use_joystick)
1085 {
1086 do
1087 {
1088 any_down = 0;
1089
1090 SDL_PollEvent(&event);
1091
1092 for (i = 0; i < SDL_JoystickNumButtons(js); i++)
1093 {
1094 if (SDL_JoystickGetButton(js, i))
1095 any_down = 1;
1096 }
1097
1098 SDL_Delay(10);
1099 }
1100 while (any_down);
1101 }
1102 }
1103
1104 #endif
1105
1106
1107 /* Show the title... let them change the level: */
1108
title(void)1109 int title(void)
1110 {
1111 #ifndef EMBEDDED
1112 int btn;
1113 #endif
1114 int quit, ok, title_width, title_height, height,
1115 author_width, author_height, author_x, author_y, author,
1116 author_xm, author_ym, sine_timer, i, credits_width, credits_height;
1117 SDL_Rect src, dest;
1118 Uint32 last_time;
1119
1120
1121 /* Find out size of bitmaps we're using in the title screen: */
1122
1123 title_width = object_pixmaps[TITLE_GFX - 1] -> w;
1124 title_height = object_pixmaps[TITLE_GFX - 1] -> h;
1125
1126 author_width = object_pixmaps[AUTHOR_GFX - 1] -> w;
1127 author_height = object_pixmaps[AUTHOR_GFX - 1] -> h;
1128
1129 credits_width = object_pixmaps[CREDITS_GFX - 1] -> w;
1130 credits_height = object_pixmaps[CREDITS_GFX - 1] -> h;
1131
1132 author_x = WIDTH * 48 - author_width;
1133 author_y = HEIGHT * 48 - author_height - (rand() % 10) - 30;
1134
1135
1136 /* Clear the screen: */
1137
1138 erasewindow();
1139
1140
1141 /* Draw credits / title / version: */
1142
1143 dest.x = (WIDTH * 48 - credits_width) / 2;
1144 dest.y = (HEIGHT * 48 - credits_height) / 2;
1145 dest.w = credits_width;
1146 dest.h = credits_height;
1147
1148 SDL_BlitSurface(object_pixmaps[CREDITS_GFX - 1], NULL,
1149 window, &dest);
1150
1151 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
1152
1153
1154 /* Draw the currently-selected level: */
1155
1156 updatelevel();
1157
1158
1159 /* Init. animation variables: */
1160
1161 author = 0;
1162 height = 0;
1163 do
1164 {
1165 author_xm = -(rand() % 3);
1166 }
1167 while (author_xm == 0);
1168 author_ym = (rand() % 6) - 3;
1169
1170
1171 /* We haven't quit and we haven't started a game: */
1172
1173 quit = NO;
1174 ok = NO;
1175
1176
1177 /* Title animation loop: */
1178
1179 sine_timer = 500 - title_height;
1180
1181 do
1182 {
1183 last_time = SDL_GetTicks();
1184
1185
1186 /* Show more and more of the title: */
1187
1188 height = height + 2;
1189 if (height > title_height)
1190 height = title_height;
1191
1192
1193 /* Draw it: */
1194
1195 sine_timer++;
1196
1197 if (sine_timer < 500)
1198 {
1199 dest.x = (WIDTH * 48 - title_width) / 2;
1200 dest.y = -title_height + height;
1201 dest.w = title_width;
1202 dest.h = title_height;
1203
1204 SDL_BlitSurface(object_pixmaps[TITLE_GFX - 1], NULL,
1205 window, &dest);
1206
1207 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
1208 }
1209 else
1210 {
1211 for (i = 0; i < title_height; i++)
1212 {
1213 src.x = 0;
1214 src.y = i;
1215 src.w = title_width;
1216 src.h = 1;
1217
1218 dest.x = (int) ((WIDTH * 48 - title_width) / 2 +
1219 sin(M_PI * ((i + sine_timer) * 5) / 180.0) * 5.0);
1220 dest.y = i;
1221 dest.w = title_width;
1222 dest.h = 1;
1223
1224 SDL_BlitSurface(object_pixmaps[TITLE_GFX - 1], &src,
1225 window, &dest);
1226
1227 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
1228 }
1229 }
1230
1231 /* Hmm!? */
1232
1233 author = author + 1;
1234
1235 if (author > 1000)
1236 {
1237 dest.x = author_x;
1238 dest.y = author_y;
1239 dest.w = author_width;
1240 dest.h = author_height;
1241
1242 my_FillRect(window, &dest, 0x00, 0x00, 0x00);
1243
1244 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
1245
1246 author_x = author_x + author_xm;
1247 if (author_x < 0)
1248 {
1249 author_x = 0;
1250 do
1251 {
1252 author_xm = rand() % 3;
1253 }
1254 while (author_xm == 0);
1255 }
1256 if (author_x > WIDTH * 48 - author_width)
1257 {
1258 author_x = WIDTH * 48 - author_width;
1259 do
1260 {
1261 author_xm = -rand() % 3;
1262 }
1263 while (author_xm == 0);
1264 }
1265
1266 author_y = author_y + author_ym;
1267 if (author_y < windowheight / 2)
1268 {
1269 author_y = windowheight / 2;
1270 }
1271 if (author_y > HEIGHT * 48 - author_height)
1272 {
1273 author_y = HEIGHT * 48 - author_height;
1274 author_ym = -author_ym;
1275 }
1276
1277 author_ym = author_ym + 1;
1278 if (author_ym > 10)
1279 author_ym = 10;
1280
1281 if (author < 1500)
1282 {
1283 dest.x = author_x;
1284 dest.y = author_y;
1285 dest.w = author_width;
1286 dest.h = author_height;
1287
1288 SDL_BlitSurface(object_pixmaps[AUTHOR_GFX - 1], NULL,
1289 window, &dest);
1290
1291 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
1292 }
1293 else
1294 {
1295 author = 0;
1296 }
1297 }
1298
1299
1300 /* Check for keypresses: */
1301
1302 getkey(0);
1303
1304 if (key == SDLK_ESCAPE ||
1305 key == SDLK_q)
1306 {
1307 /* Escape to quit: */
1308
1309 ok = YES;
1310 quit = YES;
1311 }
1312 else if (key == SDLK_l)
1313 {
1314 /* L for level select: */
1315
1316 level = level + 1;
1317 if (level > max_level)
1318 level = 1;
1319
1320 playsound(SND_LEVELSELECT);
1321
1322 updatelevel();
1323 }
1324 else if (key == SDLK_RETURN ||
1325 key == SDLK_SPACE)
1326 {
1327 /* Return or Space to begin: */
1328
1329 ok = YES;
1330 }
1331 else if (key == SDLK_a)
1332 {
1333 /* Hmm!? */
1334
1335 if (author < 1000)
1336 author = 1000;
1337 else
1338 author = 1495;
1339 }
1340
1341
1342 /* Any joystick buttons? */
1343
1344 #ifndef EMBEDDED
1345 if (use_joystick)
1346 {
1347 for (btn = 0; btn < SDL_JoystickNumButtons(js); btn++)
1348 {
1349 if (SDL_JoystickGetButton(js, btn))
1350 ok = YES;
1351 }
1352 }
1353 #endif
1354
1355
1356 /* Keep framerate exact: */
1357
1358 if (SDL_GetTicks() < last_time + (1000 / FPS))
1359 SDL_Delay(last_time + (1000 / FPS) - SDL_GetTicks());
1360
1361
1362 /* Play music: */
1363 #ifndef NOSOUND
1364 if (use_sound)
1365 {
1366 if (no_music == 0)
1367 {
1368 if (!Mix_PlayingMusic())
1369 Mix_PlayMusic(songs[MOD_TITLE], 0);
1370 /* Mix_VolumeMusic(8); */
1371 }
1372 }
1373 #endif
1374 }
1375 while (ok != YES);
1376
1377 #ifndef EMBEDDED
1378 eatjoybuttons();
1379 #endif
1380
1381 return(quit);
1382 }
1383
1384
1385 /* See if you're happy or sad... Depends on if there are any blocks
1386 close to the bottom: */
1387
checkhappy()1388 void checkhappy()
1389 {
1390 int x, y;
1391
1392 happy = YES;
1393 for (y = HEIGHT - (HEIGHT / 3); y < HEIGHT; y++)
1394 {
1395 for (x = 0; x < WIDTH; x++)
1396 {
1397 if (blocks[y][x] != 0)
1398 happy = NO;
1399 }
1400 }
1401 }
1402
1403
1404 /* Init this level (randomly place blocks, etc.) */
1405
initlevel(void)1406 void initlevel(void)
1407 {
1408 int x, y, yy;
1409
1410
1411 playsound(SND_BEGIN);
1412
1413
1414 /* Erase all blocks: */
1415
1416 for (y = 0; y < HEIGHT; y++)
1417 for (x = 0; x < WIDTH; x++)
1418 blocks[y][x] = 0;
1419
1420
1421 /* How low should they go? */
1422
1423 yy = level;
1424 if (level > 14)
1425 yy = yy - (HEIGHT - 2);
1426
1427 if (yy > HEIGHT - 2)
1428 yy = HEIGHT - 2;
1429
1430
1431 /* Place random blocks: */
1432
1433 for (y = 0; y <= yy; y++)
1434 for (x = 0; x < WIDTH; x++)
1435 blocks[y][x] = randblock();
1436
1437
1438 /* Draw all blocks: */
1439
1440 for (y = 0; y < HEIGHT; y++)
1441 for (x = 0; x < WIDTH; x++)
1442 drawblock(x, y);
1443
1444
1445 /* Init level variables: */
1446
1447 leveldone = NO;
1448
1449 carrying = 0;
1450 howmany = 0;
1451 frozen = 0;
1452 _warning = NO;
1453 firstround = NO;
1454
1455
1456 /* Start out happy or not, depending on where the pieces are: */
1457
1458 checkhappy();
1459
1460
1461 /* How many lines do you need / have? */
1462
1463 lines = 0;
1464 linesneeded = (level * 3) + 2;
1465
1466
1467 /* Draw the rest of the game screen stuff: */
1468
1469 drawscore();
1470 updatelines();
1471 drawstatus();
1472 updatelevel();
1473 }
1474
1475
1476 /* Erase player: */
1477
eraseyou(int x)1478 void eraseyou(int x)
1479 {
1480 SDL_Rect dest;
1481
1482 dest.x = x * 48 - 48;
1483 dest.y = HEIGHT * 48;
1484 dest.w = 48 * 3;
1485 dest.h = 48;
1486
1487 my_FillRect(window, &dest, 0x00, 0x00, 0x00);
1488
1489 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
1490 }
1491
1492
1493 /* Draw player and objects he's carrying: */
1494
drawyou(int x)1495 void drawyou(int x)
1496 {
1497 int y;
1498
1499
1500 y = HEIGHT * 48;
1501
1502 if (howmany < 3)
1503 {
1504 /* Happy? Or sad? */
1505
1506 if (happy == YES)
1507 {
1508 drawmangraphic(x * 48, y, HAPPY_GFX);
1509 }
1510 else
1511 {
1512 drawmangraphic(x * 48, y, SAD_GFX);
1513 }
1514 }
1515
1516
1517 if (carrying != 0)
1518 {
1519 drawblockgraphic(x * 48 + 48, y, carrying);
1520
1521 if (howmany >= 2)
1522 drawblockgraphic(x * 48 - 48, y, carrying);
1523
1524 if (howmany >= 3)
1525 drawblockgraphic(x * 48, y, carrying);
1526 }
1527 }
1528
1529
1530 /* Assign an explosion: */
1531
explodeblock(int x,int y,int explosionwait)1532 void explodeblock(int x, int y, int explosionwait)
1533 {
1534 /* Pick an explosion entity to use: */
1535
1536 whichexplosion = whichexplosion + 1;
1537
1538 if (whichexplosion == NUM_EXPLOSIONS)
1539 whichexplosion = 0;
1540
1541
1542 /* Init. its values: */
1543
1544 explosions[whichexplosion].exist = 1;
1545 explosions[whichexplosion].anim = -explosionwait;
1546 explosions[whichexplosion].animrate = 0;
1547 explosions[whichexplosion].x = x;
1548 explosions[whichexplosion].y = y;
1549 }
1550
1551
1552 /* See if two objects match: */
1553
same(int a,int b)1554 int same(int a, int b)
1555 {
1556 int match;
1557
1558
1559 /* A match if it's the same, or the object (A) is a special object: */
1560
1561 match = NO;
1562
1563 if (a == b || a > NUM_ALL_BLOCKS || b > NUM_ALL_BLOCKS)
1564 match = YES;
1565
1566 return(match);
1567 }
1568
1569
1570 /* Kill a block (may call itself recursively!) */
1571
killblock(int x,int y,int killed)1572 int killblock(int x, int y, int killed)
1573 {
1574 int c, explosionwait;
1575
1576
1577 /* Values we use a lot later: */
1578
1579 explosionwait = killed;
1580 c = blocks[y][x];
1581
1582
1583 if (c != 0)
1584 {
1585 /* Remove the block: */
1586
1587 blocks[y][x] = 0;
1588
1589
1590 /* Put an explosion there: */
1591
1592 explodeblock(x, y, explosionwait);
1593
1594
1595 if (c == BOMB)
1596 {
1597 /* If it's a bomb, explode stuff next to it: */
1598
1599 playsound(SND_BOMB);
1600
1601 if (y > 0)
1602 {
1603 blocks[y - 1][x] = 0;
1604 explodeblock(x, y - 1, explosionwait);
1605 }
1606
1607 if (y < HEIGHT - 1)
1608 {
1609 blocks[y + 1][x] = 0;
1610 explodeblock(x, y + 1, explosionwait);
1611 }
1612
1613 if (x > 0)
1614 {
1615 blocks[y][x - 1] = 0;
1616 explodeblock(x - 1, y, explosionwait);
1617 }
1618
1619 if (x < WIDTH - 1)
1620 {
1621 blocks[y][x + 1] = 0;
1622 explodeblock(x + 1, y, explosionwait);
1623 }
1624 }
1625 else if (c == CLOCK)
1626 {
1627 /* Hit a clock! Freeze the game! */
1628
1629 playsound(SND_CLOCK);
1630
1631 frozen = 50;
1632 }
1633 else if (c == WILDCARD)
1634 {
1635 /* Hit a wildcard - play a noise... the rest comes natural: */
1636
1637 playsound(SND_WILDCARD);
1638 }
1639
1640
1641 /* Kill stuff next to you if they match! */
1642
1643 if (y > 0)
1644 {
1645 if (same(blocks[y - 1][x], c) == YES)
1646 killed = killblock(x, y - 1, killed + 1);
1647 }
1648
1649 if (y < HEIGHT - 1)
1650 {
1651 if (same(blocks[y + 1][x], c) == YES)
1652 killed = killblock(x, y + 1, killed + 1);
1653 }
1654
1655 if (x > 0)
1656 {
1657 if (same(blocks[y][x - 1], c) == YES)
1658 killed = killblock(x - 1, y, killed + 1);
1659 }
1660
1661 if (x < WIDTH - 1)
1662 {
1663 if (same(blocks[y][x + 1], c) == YES)
1664 killed = killblock(x + 1, y, killed + 1);
1665 }
1666 }
1667
1668 if (killed > 14)
1669 killed = 14;
1670
1671 return(killed);
1672 }
1673
1674
1675 /* Throw your gems: */
1676
_throw(int x)1677 void _throw(int x)
1678 {
1679 int lasty, killed;
1680 int y, c, last, nextlast, ytop, ybot, ok, doit;
1681
1682
1683 if (carrying != 0)
1684 {
1685 last = 0;
1686 nextlast = 0;
1687 lasty = -1;
1688
1689
1690 /* Find the lowest point that they'll attach to: */
1691
1692 for (y = 0; y < HEIGHT; y++)
1693 {
1694 c = blocks[y][x];
1695
1696 if (c != 0)
1697 {
1698 nextlast = last;
1699 last = c;
1700 lasty = y;
1701 }
1702 }
1703
1704
1705 /* See if there's a match: */
1706
1707 ok = NO;
1708
1709 if (same(last, carrying) == YES)
1710 {
1711 if (same(nextlast, carrying) == YES || howmany > 1)
1712 ok = YES;
1713 }
1714
1715 doit = YES;
1716
1717 ybot = lasty + 1 + howmany - 1;
1718
1719
1720 /* Don't go past the bottom: */
1721
1722 if (ybot > 10)
1723 {
1724 doit = NO;
1725 ybot = 10;
1726 if (ok == YES || howmany > 2)
1727 doit = YES;
1728 }
1729
1730
1731 /* These'll fit? Put them on the screen: */
1732
1733 if (doit == YES)
1734 {
1735 for (y = lasty + 1; y <= ybot; y++)
1736 {
1737 blocks[y][x] = carrying;
1738 drawblock(x, y);
1739 }
1740 }
1741
1742
1743 /* See if it's a match of 3 or more: */
1744
1745 ok = NO;
1746
1747 if (same(last, carrying) == YES)
1748 {
1749 if (same(nextlast, carrying) == YES || howmany > 1)
1750 ok = YES;
1751 }
1752
1753 if (howmany > 2)
1754 ok = YES;
1755
1756
1757 /* It IS! */
1758
1759 if (ok == YES)
1760 {
1761 ytop = 0;
1762 for (y = 0; y <= ybot; y++)
1763 {
1764 if (blocks[y][x] != carrying)
1765 ytop = y + 1;
1766 }
1767
1768 killed = killblock(x, ybot, 1);
1769
1770 score = score + scorevals[killed];
1771
1772 lines = lines + 1;
1773
1774 drawscore();
1775 updatelines();
1776 playsound(SND_GOT);
1777 }
1778
1779 if (doit == YES)
1780 {
1781 carrying = 0;
1782 howmany = 0;
1783
1784 playsound(SND_THROW);
1785 }
1786 }
1787 else
1788 {
1789 complain();
1790 }
1791
1792
1793 /* See if the level's done!? */
1794
1795 if (lines >= linesneeded)
1796 leveldone = YES;
1797 }
1798
1799
1800 /* Grab some gems */
1801
grab(int x)1802 void grab(int x)
1803 {
1804 int y, c, last, lasty, ok;
1805
1806
1807 /* Find the lowest gem on the column: */
1808
1809 last = 0;
1810 lasty = 0;
1811
1812 for (y = 0; y < HEIGHT; y++)
1813 {
1814 c = blocks[y][x];
1815 if (c != 0)
1816 {
1817 last = c;
1818 lasty = y;
1819 }
1820 }
1821
1822
1823 /* See if we got one: */
1824
1825 if (last == 0 || last > NUM_ALL_BLOCKS)
1826 {
1827 /* Nope! */
1828
1829 complain();
1830 }
1831 else
1832 {
1833 /* Yep!: */
1834
1835 if (last != carrying && carrying != 0)
1836 {
1837 /* Not the same as what we're carrying, though! */
1838
1839 complain();
1840 }
1841 else
1842 {
1843 /* Grab it: */
1844
1845 playsound(SND_GRAB);
1846
1847 carrying = last;
1848 blocks[lasty][x] = 0;
1849 drawblock(x, lasty);
1850 howmany = howmany + 1;
1851
1852
1853 /* Grab any of the same type that are above it: */
1854
1855 do
1856 {
1857 ok=0;
1858 lasty = lasty - 1;
1859 if (lasty >= 0)
1860 {
1861 if (blocks[lasty][x] == last)
1862 {
1863 blocks[lasty][x] = 0;
1864 drawblock(x, lasty);
1865 ok = 1;
1866 howmany = howmany + 1;
1867 }
1868 }
1869 }
1870 while (ok != 0);
1871 }
1872 }
1873 }
1874
1875
1876 /* Add more gems at the top of the screen: */
1877
addmore()1878 void addmore()
1879 {
1880 int x, y;
1881
1882
1883 /* See if the game's over: */
1884
1885 for (x = 0; x < WIDTH; x++)
1886 {
1887 if (blocks[HEIGHT - 1][x] != 0)
1888 gameover = YES;
1889 }
1890
1891
1892 if (gameover == NO)
1893 {
1894 /* Push the existing ones down: */
1895
1896 for (y = HEIGHT - 1; y > 0; y--)
1897 {
1898 for (x = 0; x < WIDTH; x++)
1899 {
1900 blocks[y][x] = blocks[y - 1][x];
1901 drawblock(x, y);
1902 }
1903 }
1904
1905
1906 /* Add random ones to the top: */
1907
1908 for (x = 0; x < WIDTH; x++)
1909 {
1910 blocks[0][x] = randblock();
1911 drawblock(x, 0);
1912 }
1913 }
1914 }
1915
1916
1917 /* Update the explosion animations: */
1918
drawexplosions(void)1919 int drawexplosions(void)
1920 {
1921 int i, any;
1922
1923
1924 /* We'll say if there are any more or not: */
1925
1926 any = 0;
1927
1928
1929 for (i = 0; i < NUM_EXPLOSIONS; i++)
1930 {
1931 if (explosions[i].exist == YES)
1932 {
1933 /* (There are some!) */
1934
1935 any = 1;
1936
1937
1938 /* There's an animation slow-down... */
1939
1940 explosions[i].animrate++;
1941
1942 if (explosions[i].animrate >= EXPLOSION_SLOW_VAL)
1943 {
1944 explosions[i].animrate = 0;
1945 explosions[i].anim++;
1946
1947 if (explosions[i].anim >= NUM_EXPLOSION_GFX)
1948 {
1949 /* It's gone! Draw whatever was/is behind it: */
1950
1951 explosions[i].exist = NO;
1952 drawblock(explosions[i].x, explosions[i].y);
1953 }
1954 else if (explosions[i].anim >= 0)
1955 {
1956 /* Draw the animation frame: */
1957
1958 drawblockgraphic(explosions[i].x * 48, explosions[i].y * 48,
1959 NUM_ALL_BLOCKS + NUM_SPECIALS + 1 +
1960 explosions[i].anim);
1961 }
1962 }
1963 }
1964 }
1965
1966 return(any);
1967 }
1968
1969
1970 /* End-of-level effect (major destruction, dude!) */
1971
levelendfx(int yourx)1972 void levelendfx(int yourx)
1973 {
1974 int x, y, any, win;
1975 Uint32 last_time;
1976
1977
1978 /* Let all of old the explosions "fizzle out"... */
1979
1980 do
1981 {
1982 last_time = SDL_GetTicks();
1983
1984
1985 /* Update explosions: */
1986
1987 any = drawexplosions();
1988
1989
1990 /* Keep framerate exact: */
1991
1992 if (SDL_GetTicks() < last_time + (1000 / FPS))
1993 SDL_Delay(last_time + (1000 / FPS) - SDL_GetTicks());
1994 }
1995 while(any == 1);
1996
1997
1998 /* Toggle value for winning animation: */
1999
2000 win = 0;
2001
2002
2003 /* Explode from the top to the bottom: */
2004
2005 playsound(SND_WIN);
2006
2007 for (y = 0; y < HEIGHT; y++)
2008 {
2009 last_time = SDL_GetTicks();
2010
2011
2012 /* Erase this row: */
2013
2014 for (x = 0; x < WIDTH; x++)
2015 {
2016 blocks[y][x] = 0;
2017 drawblock(x, y);
2018 }
2019
2020
2021 /* Add some explosions: */
2022
2023 for (x = 0; x < WIDTH; x++)
2024 {
2025 if ((rand() % 10) < 6)
2026 explodeblock(x, y, -(rand() % 10));
2027 }
2028
2029
2030 /* Animate the dude: */
2031
2032 win = 1 - win;
2033 drawmangraphic(yourx * 48, HEIGHT * 48, WIN1_GFX + win);
2034
2035
2036 /* Update the explosions: */
2037
2038 drawexplosions();
2039
2040
2041 /* Keep framerate exact: */
2042
2043 if (SDL_GetTicks() < last_time + (1000 / FPS))
2044 SDL_Delay(last_time + (1000 / FPS) - SDL_GetTicks());
2045 }
2046
2047
2048 /* Let all of the explosions "fizzle out"... */
2049
2050 do
2051 {
2052 last_time = SDL_GetTicks();
2053
2054
2055 /* Update explosions: */
2056
2057 any = drawexplosions();
2058
2059
2060 /* Draw man: */
2061
2062 win = 1 - win;
2063 drawmangraphic(yourx * 48, HEIGHT * 48, WIN1_GFX + win);
2064
2065
2066 /* Keep framerate exact: */
2067
2068 if (SDL_GetTicks() < last_time + (1000 / FPS))
2069 SDL_Delay(last_time + (1000 / FPS) - SDL_GetTicks());
2070 }
2071 while(any == 1);
2072 }
2073
2074
2075 /* Level 15 effect */
2076
level15fx()2077 void level15fx()
2078 {
2079 int x;
2080 Uint32 last_time;
2081 SDL_Rect dest;
2082
2083
2084 erasewindow();
2085
2086 for (x = windowwidth; x >= -48; x = x - 8)
2087 {
2088 last_time = SDL_GetTicks();
2089
2090
2091 /* Draw player: */
2092
2093 howmany = 2;
2094
2095 dest.x = x;
2096 dest.y = windowheight / 2 - 16;
2097 dest.w = 56;
2098 dest.h = 64;
2099
2100 my_FillRect(window, &dest, 0x00, 0x00, 0x00);
2101 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
2102
2103 drawmangraphic(x, windowheight / 2 - 16, SAD_GFX);
2104
2105 dest.x = x;
2106 dest.y = windowheight / 2 - 16 + 48;
2107 dest.w = 48;
2108 dest.h = 16;
2109
2110 SDL_BlitSurface(object_pixmaps[UHOH_GFX - 1], NULL,
2111 window, &dest);
2112
2113
2114 /* Keep framerate exact: */
2115
2116 if (SDL_GetTicks() < last_time + (1000 / FPS))
2117 SDL_Delay(last_time + (1000 / FPS) - SDL_GetTicks());
2118 }
2119
2120 }
2121
2122
2123 /* MAIN GAME FUNCTION!!!! */
2124
play()2125 void play()
2126 {
2127 int oact, q, x, y, toggle, win;
2128 #ifndef EMBEDDED
2129 int jsx, ojsx, jsbutton0, jsbutton1, jsbutton23,
2130 ojsbutton0, ojsbutton1, ojsbutton23, jstimer;
2131 #endif
2132 Uint32 last_time;
2133 SDL_Rect dest;
2134 #ifndef NOSOUND
2135 int i;
2136 #endif
2137
2138
2139 /* Init the game variables: */
2140
2141 initlevel();
2142 clicks = 0;
2143 playerx = 5;
2144 gameover = NO;
2145 win = NO;
2146 oact = 0 ;
2147 score = 0;
2148
2149
2150 /* Draw you! */
2151
2152 drawyou(playerx);
2153
2154
2155 #ifndef EMBEDDED
2156 ojsx = 0;
2157 ojsbutton0 = 0;
2158 ojsbutton1 = 0;
2159 ojsbutton23 = 0;
2160 jstimer = 0;
2161 #endif
2162
2163
2164 /* MAIN GAME LOOP: */
2165
2166 do
2167 {
2168 last_time = SDL_GetTicks();
2169
2170
2171 /* Get any keys: */
2172
2173 getkey(1);
2174
2175 #ifndef EMBEDDED
2176 if (use_joystick)
2177 {
2178 jsx = SDL_JoystickGetAxis(js, 0);
2179
2180 if (jsx < -16384 && (ojsx >= -16384 || jstimer == STICKWAIT - 1))
2181 key = SDLK_LEFT;
2182 else if (jsx > 16384 && (ojsx <= 16384 || jstimer == STICKWAIT - 1))
2183 key = SDLK_RIGHT;
2184
2185 jsbutton0 = SDL_JoystickGetButton(js, 0);
2186 jsbutton1 = SDL_JoystickGetButton(js, 1);
2187 jsbutton23 = (SDL_JoystickGetButton(js, 2) +
2188 SDL_JoystickGetButton(js, 3));
2189
2190 if (jsbutton1 && ojsbutton1 == 0)
2191 key = SDLK_DOWN;
2192 else if (jsbutton0 && ojsbutton0 == 0)
2193 key = SDLK_UP;
2194 else if (jsbutton23 && ojsbutton23 == 0)
2195 key = SDLK_RETURN;
2196
2197 if ((jsx < -16384 && ojsx < -16384) ||
2198 (jsx > 16384 && ojsx > 16384))
2199 {
2200 jstimer++;
2201 if (jstimer >= STICKWAIT)
2202 jstimer = 0;
2203 }
2204 else
2205 jstimer = 0;
2206
2207 ojsx = jsx;
2208 ojsbutton0 = jsbutton0;
2209 ojsbutton1 = jsbutton1;
2210 ojsbutton23 = jsbutton23;
2211 }
2212 #endif
2213
2214 if (key != SDLK_LAST)
2215 {
2216 if (key == SDLK_ESCAPE ||
2217 key == SDLK_q)
2218 {
2219 /* Escape or Q to quit: */
2220
2221 gameover = YES;
2222 }
2223 else if (key == SDLK_RIGHT ||
2224 key == SDLK_l)
2225 {
2226 /* Right to move you right */
2227
2228 eraseyou(playerx);
2229 playerx++;
2230
2231
2232 /* Wrap around: */
2233
2234 if (playerx > WIDTH - 1)
2235 playerx = 0;
2236 }
2237 else if (key == SDLK_LEFT ||
2238 key == SDLK_k)
2239 {
2240 /* Left to move you left: */
2241
2242 eraseyou(playerx);
2243 playerx--;
2244
2245
2246 /* Wrap around: */
2247
2248 if (playerx < 0)
2249 playerx = WIDTH - 1;
2250 }
2251 else if (key == SDLK_UP ||
2252 key == SDLK_a)
2253 {
2254 /* Up to throw: */
2255
2256 eraseyou(playerx);
2257 _throw(playerx);
2258 }
2259 else if (key == SDLK_DOWN ||
2260 key == SDLK_z)
2261 {
2262 /* Down to grab: */
2263
2264 eraseyou(playerx);
2265 grab(playerx);
2266 }
2267 else if (key == SDLK_RETURN)
2268 {
2269 /* Return to get more gems NOW!: */
2270
2271 firstround = YES;
2272 frozen = NO;
2273 clicks = 32767;
2274 }
2275 else if (key == SDLK_SPACE ||
2276 key == SDLK_p)
2277 {
2278 /* Space or P to pause... wait for keypress to continue: */
2279
2280 eraseyou(playerx);
2281
2282 toggle = 0;
2283
2284 #ifndef NOSOUND
2285 if (use_sound)
2286 {
2287 for (i = 0; i < MIX_CHANNELS; i++)
2288 Mix_HaltChannel(i);
2289
2290 if (no_music == 0)
2291 Mix_HaltMusic();
2292 }
2293 #endif
2294
2295 do
2296 {
2297 last_time = SDL_GetTicks();
2298
2299
2300 /* Pause for a frame: */
2301
2302 getkey(1);
2303
2304
2305 /* Show the word "paused": */
2306
2307 dest.x = (WIDTH * 48 - 48) / 2;
2308 dest.y = (HEIGHT + 1) * 48;
2309 dest.w = 48;
2310 dest.h = 16;
2311
2312 SDL_BlitSurface(object_pixmaps[PAUSED_GFX - 1], NULL,
2313 window, &dest);
2314 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
2315
2316
2317 /* Keep framerate exact: */
2318
2319 if (SDL_GetTicks() < last_time + (1000 / FPS))
2320 SDL_Delay(last_time + (1000 / FPS) - SDL_GetTicks());
2321 }
2322 while (key == SDLK_LAST);
2323
2324 dest.x = (WIDTH * 48 - 48) / 2;
2325 dest.y = (HEIGHT + 1) * 48;
2326 dest.w = 48;
2327 dest.h = 16;
2328
2329 my_FillRect(window, &dest, 0x00, 0x00, 0x00);
2330 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
2331 }
2332
2333
2334 /* Draw you again, just in case: */
2335
2336 drawyou(playerx);
2337 }
2338
2339
2340 /* Draw explosions: */
2341
2342 drawexplosions();
2343
2344
2345 /* Move stuff if need be: */
2346
2347 if (frozen == 0)
2348 {
2349 /* No clock... so keep counting til we move stuff down: */
2350
2351 checkhappy();
2352
2353 clicks = clicks + 1;
2354
2355
2356 /* The higher the level, the faster stuff comes: */
2357
2358 if (level < 5)
2359 q = level * 60;
2360 else if (level < 14)
2361 q = level * 25;
2362 else if (level == 14)
2363 q = level * 24;
2364 else
2365 q = (level - 2) * 20;
2366
2367
2368 /* Warn them first... */
2369
2370 if (clicks >= 370 - q && firstround == YES)
2371 {
2372 _warning = YES;
2373 }
2374
2375
2376 /* Drop stuff down: */
2377
2378 if (clicks >= 400 - q)
2379 {
2380 if (firstround == YES)
2381 {
2382 addmore();
2383 playsound(SND_BEGIN);
2384 }
2385
2386 clicks = 0;
2387 firstround = YES;
2388
2389 _warning = NO;
2390 }
2391 }
2392 else
2393 {
2394 /* Count down the clock... */
2395
2396 happy = YES;
2397
2398 frozen = frozen - 1;
2399 }
2400
2401
2402 drawstatus();
2403
2404
2405 /* See if the level's complete! */
2406
2407 if (leveldone == YES)
2408 {
2409 /* Do the end-of-level effect: */
2410
2411 levelendfx(playerx);
2412
2413
2414 /* Increment the level: */
2415
2416 level = level + 1;
2417 if (level > 20)
2418 {
2419 gameover = YES;
2420 win = YES;
2421 }
2422
2423 if (level > max_level)
2424 max_level = level;
2425
2426
2427 /* Do the level 15 effect: */
2428
2429 if (level == 15)
2430 level15fx();
2431
2432
2433 /* Reset the level! */
2434
2435 initlevel();
2436 clicks = 0;
2437 updatelevel();
2438 }
2439
2440
2441 /* Keep framerate exact: */
2442
2443 if (SDL_GetTicks() < last_time + (1000 / FPS))
2444 SDL_Delay(last_time + (1000 / FPS) - SDL_GetTicks());
2445
2446
2447 /* Play music: */
2448 #ifndef NOSOUND
2449 if (use_sound)
2450 {
2451 if (no_music == 0)
2452 {
2453 if (!Mix_PlayingMusic())
2454 Mix_PlayMusic(songs[MOD_GAME1 + (rand() % NUM_GAME_MODS)], 0);
2455 /* Mix_VolumeMusic(4); */
2456 }
2457 }
2458 #endif
2459 }
2460 while (gameover == NO);
2461
2462
2463 #ifndef NOSOUND
2464 if (use_sound)
2465 {
2466 if (no_music == 0)
2467 Mix_HaltMusic();
2468 }
2469 #endif
2470
2471 if (win == NO)
2472 {
2473 /* End of game effect: */
2474
2475 playsound(SND_GAMEOVER);
2476
2477 happy = NO;
2478 drawyou(playerx);
2479
2480
2481 /* Dump bricks down at you!: */
2482
2483 for (y = 0; y < HEIGHT; y++)
2484 {
2485 last_time = SDL_GetTicks();
2486
2487 for (x = 0; x < WIDTH; x++)
2488 {
2489 drawblockgraphic(x * 48, y * 48, BRICK_GFX);
2490 }
2491
2492
2493 /* Keep framerate exact: */
2494
2495 if (SDL_GetTicks() < last_time + (1000 / FPS))
2496 SDL_Delay(last_time + (1000 / FPS) - SDL_GetTicks());
2497 }
2498 }
2499 else
2500 {
2501 /* Win game effect: */
2502
2503 playsound(SND_APPLAUSE);
2504
2505 dest.x = windowwidth / 2 - 96;
2506 dest.y = windowheight / 2 - 32;
2507 dest.w = 192;
2508 dest.h = 64;
2509
2510 SDL_BlitSurface(object_pixmaps[CONGRATULATIONS_GFX - 1], NULL,
2511 window, &dest);
2512
2513 SDL_UpdateRect(window, dest.x, dest.y, dest.w, dest.h);
2514 }
2515
2516 /* Wait for a keypress: */
2517
2518 do
2519 {
2520 getkey(0);
2521 }
2522 while (key == SDLK_LAST);
2523
2524 #ifndef EMBEDDED
2525 eatjoybuttons();
2526 #endif
2527 }
2528
2529
2530 /* Quit the application: */
2531
quitapp()2532 void quitapp()
2533 {
2534 #ifndef EMBEDDED
2535 if (use_joystick)
2536 SDL_JoystickClose(js);
2537
2538 SDL_Quit();
2539 #endif
2540 }
2541
2542
2543 /* Usage error, and quit: */
2544
usage(int ret)2545 void usage(int ret)
2546 {
2547 fprintf(stderr, "Usage: gemdropx [--nomusic] | --version | --help | --usage\n\n");
2548 exit(ret);
2549 }
2550
2551
2552 /* MAIN FUNCTION */
2553
main(int argc,char * argv[])2554 int main(int argc, char * argv[])
2555 {
2556 int quit;
2557 char datafile[1024];
2558 FILE * fi;
2559 #ifndef EMBEDDED
2560 SDL_version sdlver;
2561 #endif
2562
2563
2564 no_music = 0;
2565
2566 if (argc == 2)
2567 {
2568 /* --version or --help?? */
2569
2570 if (strcmp(argv[1], "--version") == 0)
2571 {
2572 printf("\nGem Drop X 0.9\n\n");
2573 printf("February 11, 2002\n");
2574 printf("by Bill Kendrick\n");
2575 printf("New Breed Software, (c) 1997-2002\n\n");
2576
2577 printf("Most graphics by Bernhard Trummer\n");
2578 printf("bernhard.trummer@gmx.net\n\n");
2579
2580 printf("Visit our website: http://www.newbreedsoftware.com/\n\n");
2581
2582 #ifndef EMBEDDED
2583 SDL_VERSION(&sdlver);
2584 printf("SDL version %d.%d.%d\n\n", sdlver.major,
2585 sdlver.minor, sdlver.patch);
2586 #else
2587 printf("Built for Qt/Embedded\n\n");
2588 #endif
2589
2590 exit(0);
2591 }
2592 else if (strcmp(argv[1], "--help") == 0)
2593 {
2594 printf("GEM DROP X HELP\n");
2595 printf("How To Play:\n");
2596 printf(" Use the man to grab and throw gems.\n");
2597 printf(" Match 3+ in a column when you throw to get a line.\n");
2598 printf(" Get enough lines to beat the level.\n");
2599 printf(" Gems come down at you from the top.\n");
2600 printf(" If the screen fills, the game ends.\n\n");
2601
2602 printf("Special Pieces: (You can't grab them)\n");
2603 printf(" Activate them by including them in a match.\n");
2604 printf(" - Bombs explode.\n");
2605 printf(" - Clocks stop the gems from coming for a while.\n");
2606 printf(" - Wildcards (question-marks) make for more matches.\n\n");
2607
2608 printf("Controls:\n");
2609 printf(" Left/Right - Move left/right\n");
2610 printf(" K/L - Move left/right\n");
2611 printf(" Up/Down - Throw/Grab\n");
2612 printf(" A/Z - Throw/Grab\n");
2613 printf(" Return - Get more gems immediately\n");
2614 printf(" Space / P - Pause\n");
2615 printf(" S - Toggle sound\n");
2616 printf(" Q/Escape - Abort game\n\n");
2617
2618 printf("Mouse:\n");
2619 printf(" Click Left/Right of Man - Move left/right\n");
2620 printf(" Left-click Above Man - Grab\n");
2621 printf(" Right-click Above man - Throw\n\n");
2622
2623 printf("Joystick:\n");
2624 printf(" Left/Right - Move left/right\n");
2625 printf(" Button 1/2 - Throw/Grab\n");
2626 printf(" Button 3/4 - Get more gems immediately\n\n");
2627
2628 exit(0);
2629 }
2630 else if (strcmp(argv[1], "--nomusic") == 0)
2631 {
2632 no_music = 1;
2633 }
2634 else if (strcmp(argv[1], "--usage") == 0)
2635 {
2636 usage(0);
2637 }
2638 else
2639 {
2640 usage(1);
2641 }
2642 }
2643 else if (argc > 2)
2644 {
2645 /* Oops! Usage! */
2646
2647 usage(1);
2648 }
2649
2650
2651 /* Setup: */
2652
2653 setup(argc, argv);
2654
2655
2656 /* Get max level: */
2657
2658 sprintf(datafile, "%s/.gemdropx", getenv("HOME"));
2659
2660 fi = fopen(datafile, "r");
2661 if (fi != NULL)
2662 {
2663 fscanf(fi, "%d", &max_level);
2664
2665 if (max_level < 10 || max_level > 20)
2666 {
2667 fprintf(stderr, "gemdropx: %s corrupt!\n", datafile);
2668 max_level = 10;
2669 }
2670
2671 fclose(fi);
2672 }
2673 else
2674 max_level = 10;
2675
2676
2677 /* Main loop: */
2678
2679 do
2680 {
2681 game_playing = 0;
2682 quit = title();
2683
2684 #ifndef NOSOUND
2685 if (use_sound)
2686 {
2687 if (no_music == 0)
2688 Mix_HaltMusic();
2689 }
2690 #endif
2691
2692 if (quit == NO)
2693 {
2694 game_playing = 1;
2695 play();
2696 }
2697 }
2698 while (quit == NO);
2699
2700
2701 /* Save max. level: */
2702
2703 fi = fopen(datafile, "w");
2704
2705 if (fi != NULL)
2706 {
2707 fprintf(fi, "%d\n", max_level);
2708 fclose(fi);
2709 }
2710 else
2711 perror(datafile);
2712
2713 quitapp();
2714
2715 return(0);
2716 }
2717