Clean up some inconsistencies in themes.
[ntk.git] / test / blocks.cxx
blobf3eef86460af95b4f98ba6be9ac61df99899e8e6
1 //
2 // "$Id: blocks.cxx 7904 2010-11-28 21:12:59Z matt $"
3 //
4 // "Block Attack!" scrolling blocks game using the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 2006-2010 by Michael Sweet.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <FL/Fl.H>
29 #include <FL/Fl_Double_Window.H>
30 #include <FL/Fl_Button.H>
31 #include <FL/Fl_Preferences.H>
32 #include <FL/Fl_XPM_Image.H>
33 #include <FL/Fl_XBM_Image.H>
34 #include <FL/Fl_Tiled_Image.H>
35 #include <FL/fl_draw.H>
36 #include <FL/x.H>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41 #include <math.h>
43 // Audio headers...
44 #include <config.h>
46 #ifndef WIN32
47 # include <unistd.h>
48 # include <sys/time.h>
49 #endif // !WIN32
51 #ifdef HAVE_ALSA_ASOUNDLIB_H
52 # define ALSA_PCM_NEW_HW_PARAMS_API
53 # include <alsa/asoundlib.h>
54 #endif // HAVE_ALSA_ASOUNDLIB_H
55 #ifdef __APPLE__
56 # include <CoreAudio/AudioHardware.h>
57 #endif // __APPLE__
58 #ifdef WIN32
59 # include <mmsystem.h>
60 #endif // WIN32
63 #define BLOCK_COLS 20
64 #define BLOCK_ROWS 10
65 #define BLOCK_SIZE 32
66 #define BLOCK_BLAST 100
68 #include "pixmaps/blast.xpm"
69 Fl_Pixmap blast_pixmap(blast_xpm);
71 #include "pixmaps/red.xpm"
72 Fl_Pixmap red_pixmap(red_xpm);
73 #include "pixmaps/red_bomb.xpm"
74 Fl_Pixmap red_bomb_pixmap(red_bomb_xpm);
76 #include "pixmaps/green.xpm"
77 Fl_Pixmap green_pixmap(green_xpm);
78 #include "pixmaps/green_bomb.xpm"
79 Fl_Pixmap green_bomb_pixmap(green_bomb_xpm);
81 #include "pixmaps/blue.xpm"
82 Fl_Pixmap blue_pixmap(blue_xpm);
83 #include "pixmaps/blue_bomb.xpm"
84 Fl_Pixmap blue_bomb_pixmap(blue_bomb_xpm);
86 #include "pixmaps/yellow.xpm"
87 Fl_Pixmap yellow_pixmap(yellow_xpm);
88 #include "pixmaps/yellow_bomb.xpm"
89 Fl_Pixmap yellow_bomb_pixmap(yellow_bomb_xpm);
91 #include "pixmaps/cyan.xpm"
92 Fl_Pixmap cyan_pixmap(cyan_xpm);
93 #include "pixmaps/cyan_bomb.xpm"
94 Fl_Pixmap cyan_bomb_pixmap(cyan_bomb_xpm);
96 #include "pixmaps/magenta.xpm"
97 Fl_Pixmap magenta_pixmap(magenta_xpm);
98 #include "pixmaps/magenta_bomb.xpm"
99 Fl_Pixmap magenta_bomb_pixmap(magenta_bomb_xpm);
101 #include "pixmaps/gray.xpm"
102 Fl_Pixmap gray_pixmap(gray_xpm);
103 #include "pixmaps/gray_bomb.xpm"
104 Fl_Pixmap gray_bomb_pixmap(gray_bomb_xpm);
106 Fl_Pixmap *normal_pixmaps[] = {
107 &red_pixmap,
108 &green_pixmap,
109 &blue_pixmap,
110 &yellow_pixmap,
111 &cyan_pixmap,
112 &magenta_pixmap,
113 &gray_pixmap
115 Fl_Pixmap *bomb_pixmaps[] = {
116 &red_bomb_pixmap,
117 &green_bomb_pixmap,
118 &blue_bomb_pixmap,
119 &yellow_bomb_pixmap,
120 &cyan_bomb_pixmap,
121 &magenta_bomb_pixmap,
122 &gray_bomb_pixmap
125 const unsigned char screen_bits[] = {
126 0xff, 0x55, 0xff, 0xaa, 0xff, 0x55, 0xff, 0xaa
128 Fl_Bitmap screen_bitmap(screen_bits, 8, 8);
129 Fl_Tiled_Image screen_tile(&screen_bitmap);
132 // Sound class...
134 // There are MANY ways to implement sound in a FLTK application.
135 // The approach we are using here is to conditionally compile OS-
136 // specific code into the application - CoreAudio for MacOS X, the
137 // standard Win32 API stuff for Windows, ALSA or X11 for Linux, and
138 // X11 for all others. We have to support ALSA on Linux because the
139 // current Xorg releases no longer support XBell() or the PC speaker.
141 // There are several good cross-platform audio libraries we could also
142 // use, such as OpenAL, PortAudio, and SDL, however they were not chosen
143 // for this application because of our limited use of sound.
145 // Many thanks to Ian MacArthur who provided sample code that led to
146 // the CoreAudio implementation you see here!
147 class BlockSound {
148 // Private, OS-specific data...
149 #ifdef __APPLE__
150 AudioDeviceID device;
151 # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
152 AudioDeviceIOProcID audio_proc_id;
153 # endif
154 AudioStreamBasicDescription format;
155 short *data;
156 int remaining;
158 static OSStatus audio_cb(AudioDeviceID device,
159 const AudioTimeStamp *current_time,
160 const AudioBufferList *data_in,
161 const AudioTimeStamp *time_in,
162 AudioBufferList *data_out,
163 const AudioTimeStamp *time_out,
164 void *client_data);
165 #elif defined(WIN32)
166 HWAVEOUT device;
167 HGLOBAL header_handle;
168 LPWAVEHDR header_ptr;
169 HGLOBAL data_handle;
170 LPSTR data_ptr;
172 #else
173 # ifdef HAVE_ALSA_ASOUNDLIB_H
174 snd_pcm_t *handle;
175 # endif // HAVE_ALSA_ASOUNDLIB_H
176 #endif // __APPLE__
178 public:
180 // Common data...
181 static short *sample_data;
182 static int sample_size;
184 BlockSound();
185 ~BlockSound();
187 void play_explosion(float duration);
190 // Sound class globals...
191 short *BlockSound::sample_data = NULL;
192 int BlockSound::sample_size = 0;
195 // Initialize the BlockSound class
196 BlockSound::BlockSound() {
197 sample_size = 0;
199 #ifdef __APPLE__
200 remaining = 0;
202 UInt32 size = sizeof(device);
204 if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
205 &size, (void *)&device) != noErr) return;
207 size = sizeof(format);
208 if (AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyStreamFormat,
209 &size, &format) != noErr) return;
211 // Set up a format we like...
212 format.mSampleRate = 44100.0; // 44.1kHz
213 format.mChannelsPerFrame = 2; // stereo
215 if (AudioDeviceSetProperty(device, NULL, 0, false,
216 kAudioDevicePropertyStreamFormat,
217 sizeof(format), &format) != noErr) return;
219 // Check we got linear pcm - what to do if we did not ???
220 if (format.mFormatID != kAudioFormatLinearPCM) return;
222 // Attach the callback and start the device
223 # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
224 if (AudioDeviceCreateIOProcID(device, audio_cb, (void *)this, &audio_proc_id) != noErr) return;
225 AudioDeviceStart(device, audio_proc_id);
226 # else
227 if (AudioDeviceAddIOProc(device, audio_cb, (void *)this) != noErr) return;
228 AudioDeviceStart(device, audio_cb);
229 # endif
231 sample_size = (int)format.mSampleRate;
233 #elif defined(WIN32)
234 WAVEFORMATEX format;
236 memset(&format, 0, sizeof(format));
237 format.cbSize = sizeof(format);
238 format.wFormatTag = WAVE_FORMAT_PCM;
239 format.nChannels = 2;
240 format.nSamplesPerSec = 44100;
241 format.nAvgBytesPerSec = 44100 * 4;
242 format.nBlockAlign = 4;
243 format.wBitsPerSample = 16;
245 data_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, format.nSamplesPerSec * 4);
246 if (!data_handle) return;
248 data_ptr = (LPSTR)GlobalLock(data_handle);
250 header_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
251 if (!header_handle) return;
253 header_ptr = (WAVEHDR *)GlobalLock(header_handle);
255 header_ptr->lpData = data_ptr;
256 header_ptr->dwFlags = 0;
257 header_ptr->dwLoops = 0;
259 if (waveOutOpen(&device, WAVE_MAPPER, &format, 0, 0, WAVE_ALLOWSYNC)
260 != MMSYSERR_NOERROR) return;
262 sample_size = format.nSamplesPerSec;
264 #else
265 # ifdef HAVE_ALSA_ASOUNDLIB_H
266 handle = NULL;
268 if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) >= 0) {
269 // Initialize PCM sound stuff...
270 snd_pcm_hw_params_t *params;
272 snd_pcm_hw_params_alloca(&params);
273 snd_pcm_hw_params_any(handle, params);
274 snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
275 snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16);
276 snd_pcm_hw_params_set_channels(handle, params, 2);
277 unsigned rate = 44100;
278 int dir;
279 snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
280 snd_pcm_uframes_t period = (int)rate;
281 snd_pcm_hw_params_set_period_size_near(handle, params, &period, &dir);
283 sample_size = rate;
285 if (snd_pcm_hw_params(handle, params) < 0) {
286 sample_size = 0;
287 snd_pcm_close(handle);
288 handle = NULL;
291 # endif // HAVE_ALSA_ASOUNDLIB_H
292 #endif // __APPLE__
294 if (sample_size) {
295 // Make an explosion sound by passing white noise through a low pass
296 // filter with a decreasing frequency...
297 sample_data = new short[2 * sample_size];
299 short *sample_ptr = sample_data;
300 int max_sample = 2 * sample_size - 2;
302 *sample_ptr++ = 0;
303 *sample_ptr++ = 0;
305 for (int j = max_sample; j > 0; j --, sample_ptr ++) {
306 float freq = (float)j / (float)max_sample;
307 float volume = 32767.0 * (0.5 * sqrt(freq) + 0.5);
308 float sample = 0.0001 * ((rand() % 20001) - 10000);
310 *sample_ptr = (int)(volume * freq * sample +
311 (1.0 - freq) * sample_ptr[-2]);
317 // Cleanup the BlockSound class
318 BlockSound::~BlockSound() {
319 #ifdef __APPLE__
320 if (sample_size) {
321 # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
322 AudioDeviceStop(device, audio_proc_id);
323 AudioDeviceDestroyIOProcID(device, audio_proc_id);
324 # else
325 AudioDeviceStop(device, audio_cb);
326 AudioDeviceRemoveIOProc(device, audio_cb);
327 # endif
330 #elif defined(WIN32)
331 if (sample_size) {
332 waveOutClose(device);
334 GlobalUnlock(header_handle);
335 GlobalFree(header_handle);
337 GlobalUnlock(data_handle);
338 GlobalFree(data_handle);
341 #else
342 # ifdef HAVE_ALSA_ASOUNDLIB_H
343 if (handle) {
344 snd_pcm_drain(handle);
345 snd_pcm_close(handle);
347 # endif // HAVE_ALSA_ASOUNDLIB_H
348 #endif // __APPLE__
350 if (sample_size) {
351 delete[] sample_data;
356 #ifdef __APPLE__
357 // Callback function for writing audio data...
358 OSStatus
359 BlockSound::audio_cb(AudioDeviceID device,
360 const AudioTimeStamp *current_time,
361 const AudioBufferList *data_in,
362 const AudioTimeStamp *time_in,
363 AudioBufferList *data_out,
364 const AudioTimeStamp *time_out,
365 void *client_data) {
366 BlockSound *ss = (BlockSound *)client_data;
367 int count;
368 float *buffer;
370 if (!ss->remaining) return noErr;
372 for (count = data_out->mBuffers[0].mDataByteSize / sizeof(float),
373 buffer = (float*) data_out->mBuffers[0].mData;
374 ss->remaining > 0 && count > 0;
375 count --, ss->data ++, ss->remaining --) {
376 *buffer++ = *(ss->data) / 32767.0;
379 while (count > 0) {
380 *buffer++ = 0.0;
381 count --;
384 return noErr;
386 #endif // __APPLE__
389 // Play a note for the given amount of time...
390 void
391 BlockSound::play_explosion(float duration) {
392 Fl::check();
394 if (duration <= 0.0)
395 return;
397 #if defined(__APPLE__) || defined(WIN32) || defined(HAVE_ALSA_ASOUNDLIB_H)
398 if (duration > 1.0)
399 duration = 1.0;
401 int samples = (int)(duration * sample_size);
402 short *sample_ptr = sample_data + 2 * (sample_size - samples);
403 #endif // __APPLE__ || WIN32 || HAVE_ALSA_ASOUNDLIB_H
405 #ifdef __APPLE__
406 // Point to the next note...
407 data = sample_ptr;
408 remaining = samples * 2;
410 #elif defined(WIN32)
411 if (sample_size) {
412 memcpy(data_ptr, sample_ptr, samples * 4);
414 header_ptr->dwBufferLength = samples * 4;
415 waveOutPrepareHeader(device, header_ptr, sizeof(WAVEHDR));
417 waveOutWrite(device, header_ptr, sizeof(WAVEHDR));
418 } else Beep(440, (int)(1000.0 * duration));
420 #elif defined(HAVE_ALSA_ASOUNDLIB_H)
421 if (handle) {
422 // Use ALSA to play the sound...
423 if (snd_pcm_writei(handle, sample_ptr, samples) < 0) {
424 snd_pcm_prepare(handle);
425 snd_pcm_writei(handle, sample_ptr, samples);
427 return;
429 #endif // __APPLE__
433 class BlockWindow : public Fl_Double_Window
435 public:
437 struct Block
439 int color;
440 bool bomb;
441 int y;
444 struct Column
446 int num_blocks;
447 Block blocks[BLOCK_ROWS];
448 int x;
451 private:
453 Fl_Button *help_button_,
454 *play_button_;
456 int num_columns_;
457 Column columns_[BLOCK_COLS];
458 int count_;
459 bool help_;
460 int high_score_;
461 float interval_;
462 int level_;
463 int num_colors_;
464 int opened_columns_;
465 bool paused_;
466 static Fl_Preferences prefs_;
467 int score_;
468 BlockSound *sound_;
469 char title_[255];
470 int title_y_;
472 void _BlockWindow();
473 int bomb(int color);
474 int click(int col, int row);
475 static void help_cb(Fl_Widget *wi, BlockWindow *bw);
476 void init();
477 static void play_cb(Fl_Widget *wi, BlockWindow *bw);
478 static void timeout_cb(BlockWindow *bw);
480 public:
482 BlockWindow(int X, int Y, int W, int H, const char *L = 0);
483 BlockWindow(int W, int H, const char *L = 0);
484 ~BlockWindow();
486 void draw();
487 int handle(int event);
488 void new_game();
489 int score() { return (score_); }
490 void up_level();
494 Fl_Preferences BlockWindow::prefs_(Fl_Preferences::USER, "fltk.org", "blocks");
498 main(int argc, char *argv[]) {
499 Fl::scheme("plastic");
500 Fl::visible_focus(0);
502 BlockWindow *bw = new BlockWindow(BLOCK_COLS * BLOCK_SIZE,
503 BLOCK_ROWS * BLOCK_SIZE + 20,
504 "Block Attack!");
506 bw->show(argc, argv);
508 return (Fl::run());
512 // Create a block window at the specified position
513 BlockWindow::BlockWindow(int X, int Y, int W, int H, const char *L)
514 : Fl_Double_Window(X, Y, W, H, L) {
515 _BlockWindow();
519 // Create a block window
520 BlockWindow::BlockWindow(int W, int H, const char *L)
521 : Fl_Double_Window(W, H, L) {
522 _BlockWindow();
526 // Delete a block window
527 BlockWindow::~BlockWindow() {
528 Fl::remove_timeout((Fl_Timeout_Handler)timeout_cb, (void *)this);
532 // Initialize a block window...
533 void
534 BlockWindow::_BlockWindow() {
535 init();
537 help_button_ = new Fl_Button(0, 0, 20, 20, "?");
538 help_button_->callback((Fl_Callback *)help_cb, this);
539 help_button_->shortcut('?');
541 play_button_ = new Fl_Button(80, (h() - 80) / 2, 80, 80, "@>");
542 play_button_->callback((Fl_Callback *)play_cb, this);
543 play_button_->labelsize(44);
544 play_button_->shortcut(' ');
546 sound_ = new BlockSound();
548 prefs_.get("high_score", high_score_, 0);
550 Fl::add_timeout(0.1, (Fl_Timeout_Handler)timeout_cb, (void *)this);
554 // Bomb all blocks of a given color and return the number of affected blocks
556 BlockWindow::bomb(int color) {
557 int j, k;
558 int count;
559 Block *b;
560 Column *c;
563 if (color >= BLOCK_BLAST) return (0);
565 for (j = num_columns_, c = columns_, count = 1; j > 0; j --, c ++)
566 for (k = c->num_blocks, b = c->blocks; k > 0; k --, b ++)
567 if (b->color == color) {
568 b->color = -color;
569 count ++;
572 return (count);
576 // Tag all blocks connected to the clicked block and return the number
577 // of affected blocks
579 BlockWindow::click(int col, int row) {
580 Block *b;
581 Column *c;
582 int count, color;
585 c = columns_ + col;
586 b = c->blocks + row;
587 color = b->color;
589 if (color < 0 || color >= BLOCK_BLAST) return (0);
591 // Find the bottom block...
592 while (row > 0 && b[-1].color == color) {
593 row --;
594 b --;
597 count = 0;
599 while (row < c->num_blocks && b->color == color) {
600 b->color = -color;
602 if (col > 0 && row < c[-1].num_blocks &&
603 c[-1].blocks[row].color == color) {
604 count += click(col - 1, row);
607 if (col < (num_columns_ - 1) && row < c[1].num_blocks &&
608 c[1].blocks[row].color == color) {
609 count += click(col + 1, row);
612 count ++;
613 row ++;
614 b ++;
617 return (count);
621 // Draw the block window...
622 void
623 BlockWindow::draw() {
624 int j, k, xx, yy;
625 Block *b;
626 Column *c;
629 // Draw the blocks...
630 fl_color(FL_BLACK);
631 fl_rectf(0, 0, w(), h());
633 // Draw the blocks...
634 for (j = num_columns_, c = columns_; j > 0; j --, c ++)
635 for (k = c->num_blocks, b = c->blocks; k > 0; k --, b ++) {
636 xx = w() - c->x;
637 yy = h() - BLOCK_SIZE - b->y;
639 if (b->color >= BLOCK_BLAST) {
640 b->color ++;
641 blast_pixmap.draw(xx, yy);
642 } else if (b->color < 0) {
643 if (b->bomb) bomb_pixmaps[-b->color - 1]->draw(xx, yy);
644 else normal_pixmaps[-b->color - 1]->draw(xx, yy);
645 } else {
646 if (b->bomb) bomb_pixmaps[b->color - 1]->draw(xx, yy);
647 else normal_pixmaps[b->color - 1]->draw(xx, yy);
651 if (interval_ < 0.0 || paused_) {
652 fl_color(FL_BLACK);
653 screen_tile.draw(0, 0, w(), h(), 0, 0);
656 // Redraw the widgets...
657 play_button_->redraw();
658 help_button_->redraw();
659 draw_children();
661 // Draw any paused/game over/new game message...
662 if ((paused_ || interval_ < 0.0) && play_button_->w() == 80) {
663 const char *s;
665 if (help_) {
666 s = "Click on adjacent blocks of the same color. Clear all blocks "
667 "before they reach the left side.";
669 fl_font(FL_HELVETICA_BOLD, 24);
670 fl_color(FL_BLACK);
671 fl_draw(s, 171, 3, w() - 250, h() - 6,
672 (Fl_Align)(FL_ALIGN_WRAP | FL_ALIGN_LEFT));
674 fl_color(FL_YELLOW);
675 fl_draw(s, 168, 0, w() - 250, h(),
676 (Fl_Align)(FL_ALIGN_WRAP | FL_ALIGN_LEFT));
677 } else {
678 if (interval_ < 0.0) {
679 #ifdef DEBUG
680 // Show sample waveform...
681 short *sample_ptr;
683 for (i = 0; i < 2; i ++)
685 fl_color(FL_RED + i);
686 fl_begin_line();
687 for (j = 0, sample_ptr = sound_->sample_data + i;
688 j < sound_->sample_size;
689 j ++, sample_ptr += 2)
690 fl_vertex(j * w() / sound_->sample_size,
691 *sample_ptr * h() / 4 / 65534 + h() / 2);
692 fl_end_line();
694 #endif // DEBUG
696 if (num_columns_ && (time(NULL) & 7) < 4) s = "Game Over";
697 else s = "Block Attack!\nby Michael R Sweet";
698 } else s = "Paused";
700 fl_font(FL_HELVETICA_BOLD, 32);
701 fl_color(FL_BLACK);
702 fl_draw(s, 6, 6, w() - 6, h() - 6, FL_ALIGN_CENTER);
704 fl_color(FL_YELLOW);
705 fl_draw(s, 0, 0, w(), h(), FL_ALIGN_CENTER);
709 // Draw the scores and level...
710 char s[255];
712 sprintf(s, " Score: %d", score_);
713 fl_color(FL_WHITE);
714 fl_font(FL_HELVETICA, 14);
715 fl_draw(s, 40, 0, w() - 40, 20, FL_ALIGN_LEFT);
717 sprintf(s, "High Score: %d ", high_score_);
718 fl_draw(s, 0, 0, w(), 20, FL_ALIGN_RIGHT);
720 if (level_ > 1 || title_y_ <= 0)
722 sprintf(s, "Level: %d ", level_);
723 fl_draw(s, 0, 0, w(), 20, FL_ALIGN_CENTER);
726 if (title_y_ > 0 && interval_ > 0.0)
728 int sz = 14 + title_y_ * 86 / h();
730 fl_font(FL_HELVETICA_BOLD, sz);
731 fl_color(FL_YELLOW);
732 fl_draw(title_, 0, title_y_, w(), sz, FL_ALIGN_CENTER);
737 // Handle mouse clicks, etc.
739 BlockWindow::handle(int event) {
740 int j, k, mx, my, count;
741 Block *b;
742 Column *c;
745 if (Fl_Double_Window::handle(event)) return (1);
746 else if (interval_ < 0.0 || paused_) return (0);
748 switch (event) {
749 case FL_KEYBOARD:
750 if (Fl::event_text()) {
751 if (strcmp(Fl::event_text(), "+") == 0)
752 up_level();
754 break;
755 case FL_PUSH :
756 mx = w() - Fl::event_x() + BLOCK_SIZE;
757 my = h() - Fl::event_y();
758 count = 0;
759 b = 0;
761 for (j = 0, c = columns_; !count && j < num_columns_; j ++, c ++)
762 for (k = 0, b = c->blocks; !count && k < c->num_blocks; k ++, b ++)
763 if (mx >= c->x && mx < (c->x + BLOCK_SIZE) &&
764 my >= b->y && my < (b->y + BLOCK_SIZE)) {
765 if (b->bomb) count = bomb(b->color);
766 else count = click(j, k);
768 break;
771 if (count < 2) {
772 for (j = 0, c = columns_; j < num_columns_; j ++, c ++)
773 for (k = 0, b = c->blocks; k < c->num_blocks; k ++, b ++)
774 if (b->color < 0) b->color = -b->color;
775 } else {
776 count --;
778 if (b->bomb) {
779 sound_->play_explosion(0.19 + 0.005 * count);
781 interval_ *= 0.995;
782 score_ += count;
783 } else {
784 sound_->play_explosion(0.09 + 0.005 * count);
786 interval_ *= 0.999;
787 score_ += count * count;
790 if (score_ > high_score_) {
791 high_score_ = score_;
792 prefs_.set("high_score", high_score_);
795 for (j = 0, c = columns_; j < num_columns_; j ++, c ++)
796 for (k = 0, b = c->blocks; k < c->num_blocks; k ++, b ++)
797 if (b->color < 0) b->color = BLOCK_BLAST;
799 return (1);
802 return (0);
806 // Toggle the on-line help...
807 void
808 BlockWindow::help_cb(Fl_Widget *, BlockWindow *bw) {
809 bw->paused_ = bw->help_ = !bw->help_;
810 bw->play_button_->label("@>");
811 bw->redraw();
815 // Initialize the block window...
816 void
817 BlockWindow::init() {
818 count_ = 0;
819 help_ = false;
820 interval_ = -1.0;
821 level_ = 1;
822 num_colors_ = 3;
823 num_columns_ = 0;
824 paused_ = false;
825 score_ = 0;
826 title_[0] = '\0';
827 title_y_ = 0;
831 // Start a new game...
832 void
833 BlockWindow::new_game() {
834 // Seed the random number generator...
835 srand(time(NULL));
837 init();
839 interval_ = 0.1;
840 opened_columns_ = 0;
842 strcpy(title_, "Level: 1");
843 title_y_ = h();
845 redraw();
849 // Play/pause...
850 void
851 BlockWindow::play_cb(Fl_Widget *wi, BlockWindow *bw) {
852 if (bw->interval_ < 0) bw->new_game();
853 else bw->paused_ = !bw->paused_;
855 if (bw->paused_) wi->label("@>");
856 else {
857 wi->label("@-2||");
858 bw->help_ = false;
862 void BlockWindow::up_level() {
863 interval_ *= 0.95;
864 opened_columns_ = 0;
865 if (num_colors_ < 7) num_colors_ ++;
866 level_ ++;
867 sprintf(title_, "Level: %d", level_);
868 title_y_ = h();
869 Fl::repeat_timeout(interval_, (Fl_Timeout_Handler)timeout_cb, (void *)this);
872 // Animate the game...
873 void
874 BlockWindow::timeout_cb(BlockWindow *bw) {
875 int i, j;
876 Block *b;
877 Column *c;
878 int lastx, lasty;
881 #ifdef DEBUG
882 struct timeval curtime;
883 static struct timeval lasttime;
886 gettimeofday(&curtime, NULL);
887 printf("%.3f (%+f - %f)\n",
888 curtime.tv_sec + 0.000001 * curtime.tv_usec,
889 curtime.tv_sec - lasttime.tv_sec +
890 0.000001 * (curtime.tv_usec - lasttime.tv_usec), bw->interval_);
891 lasttime = curtime;
892 #endif // DEBUG
894 // Update blocks that have been destroyed...
895 for (i = 0, c = bw->columns_; i < bw->num_columns_; i ++, c ++)
896 for (j = 0, b = c->blocks; j < c->num_blocks; j ++, b ++)
897 if (b->color > (BLOCK_BLAST + 1)) {
898 bw->redraw();
900 c->num_blocks --;
902 if (j < c->num_blocks) {
903 memmove(b, b + 1, (c->num_blocks - j) * sizeof(Block));
906 j --;
907 b --;
909 if (!c->num_blocks) {
910 bw->num_columns_ --;
912 if (i < bw->num_columns_) {
913 memmove(c, c + 1, (bw->num_columns_ - i) * sizeof(Column));
916 i --;
917 c --;
918 j = c->num_blocks;
922 // Let the rest of the blocks fall and/or move...
923 for (i = bw->num_columns_, c = bw->columns_, lastx = c->x;
924 i > 0;
925 i --, c ++) {
926 if (c->x > lastx) {
927 c->x -= 8;
928 bw->redraw();
931 lastx = c->x + BLOCK_SIZE;
933 if (!bw->paused_ && bw->interval_ > 0.0) {
934 bw->redraw();
935 c->x ++;
938 for (j = c->num_blocks, b = c->blocks, lasty = 0; j > 0; j --, b ++) {
939 if (b->y > lasty) {
940 bw->redraw();
941 b->y -= 8;
944 lasty = b->y + BLOCK_SIZE;
948 // Slide the title text as needed...
949 if (bw->title_y_ > 0) {
950 bw->redraw();
951 bw->title_y_ -= 5;
954 // Play the game...
955 if (!bw->paused_ && bw->interval_ > 0.0) {
956 bw->count_ --;
958 if (bw->count_ <= 0) {
959 bw->redraw();
960 bw->count_ = BLOCK_SIZE;
962 if (bw->num_columns_ == BLOCK_COLS) {
963 bw->interval_ = -1.0;
964 bw->sound_->play_explosion(0.8);
965 bw->play_button_->label("@>");
966 } else {
967 bw->opened_columns_ ++;
969 if (bw->opened_columns_ > (2 * BLOCK_COLS)) {
970 bw->up_level();
973 c = bw->columns_;
975 if (bw->num_columns_) {
976 memmove(c + 1, c, bw->num_columns_ * sizeof(Column));
979 bw->num_columns_ ++;
980 c->x = 0;
981 c->num_blocks = BLOCK_ROWS;
983 for (j = 0, b = c->blocks; j < BLOCK_ROWS; j ++, b ++) {
984 b->bomb = bw->num_colors_ > 3 && (rand() & 127) < bw->num_colors_;
985 b->color = 1 + (rand() % bw->num_colors_);
986 b->y = j * (BLOCK_SIZE + 8) + 24;
991 else
993 bw->count_ --;
995 if (bw->count_ <= 0) {
996 bw->count_ = 40;
997 bw->redraw();
1001 // Update the play/pause button as needed...
1002 if ((bw->paused_ || bw->interval_< 0.0) &&
1003 bw->play_button_->w() < 80) {
1004 int s = bw->play_button_->w() + 10;
1006 bw->play_button_->resize(s, (s - 20) * (bw->h() - s) / 120, s, s);
1007 bw->play_button_->labelsize(s / 2 + 4);
1008 bw->redraw();
1009 } else if ((!bw->paused_ && bw->interval_ > 0.0) &&
1010 bw->play_button_->w() > 20) {
1011 int s = bw->play_button_->w() - 5;
1013 bw->play_button_->resize(s, (s - 20) * (bw->h() - s) / 120, s, s);
1014 bw->play_button_->labelsize(s / 2 + 4);
1015 bw->redraw();
1018 if (bw->interval_ > 0.0) {
1019 Fl::repeat_timeout(bw->interval_, (Fl_Timeout_Handler)timeout_cb,
1020 (void *)bw);
1021 } else {
1022 Fl::repeat_timeout(0.1, (Fl_Timeout_Handler)timeout_cb,
1023 (void *)bw);
1029 // End of "$Id: blocks.cxx 7904 2010-11-28 21:12:59Z matt $".