You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1031 lines
24KB

  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.
  12. //
  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.
  17. //
  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.
  22. //
  23. // Please report all bugs and problems on the following page:
  24. //
  25. // http://www.fltk.org/str.php
  26. //
  27. #include <FL/Fl.H>
  28. #include <FL/Fl_Double_Window.H>
  29. #include <FL/Fl_Button.H>
  30. #include <FL/Fl_Preferences.H>
  31. #include <FL/Fl_XPM_Image.H>
  32. #include <FL/Fl_XBM_Image.H>
  33. #include <FL/Fl_Tiled_Image.H>
  34. #include <FL/fl_draw.H>
  35. #include <FL/x.H>
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include <time.h>
  40. #include <math.h>
  41. // Audio headers...
  42. #include <config.h>
  43. #ifndef WIN32
  44. # include <unistd.h>
  45. # include <sys/time.h>
  46. #endif // !WIN32
  47. #ifdef HAVE_ALSA_ASOUNDLIB_H
  48. # define ALSA_PCM_NEW_HW_PARAMS_API
  49. # include <alsa/asoundlib.h>
  50. #endif // HAVE_ALSA_ASOUNDLIB_H
  51. #ifdef __APPLE__
  52. # include <CoreAudio/AudioHardware.h>
  53. #endif // __APPLE__
  54. #ifdef WIN32
  55. # include <mmsystem.h>
  56. #endif // WIN32
  57. #define BLOCK_COLS 20
  58. #define BLOCK_ROWS 10
  59. #define BLOCK_SIZE 32
  60. #define BLOCK_BLAST 100
  61. #include "pixmaps/blast.xpm"
  62. Fl_Pixmap blast_pixmap(blast_xpm);
  63. #include "pixmaps/red.xpm"
  64. Fl_Pixmap red_pixmap(red_xpm);
  65. #include "pixmaps/red_bomb.xpm"
  66. Fl_Pixmap red_bomb_pixmap(red_bomb_xpm);
  67. #include "pixmaps/green.xpm"
  68. Fl_Pixmap green_pixmap(green_xpm);
  69. #include "pixmaps/green_bomb.xpm"
  70. Fl_Pixmap green_bomb_pixmap(green_bomb_xpm);
  71. #include "pixmaps/blue.xpm"
  72. Fl_Pixmap blue_pixmap(blue_xpm);
  73. #include "pixmaps/blue_bomb.xpm"
  74. Fl_Pixmap blue_bomb_pixmap(blue_bomb_xpm);
  75. #include "pixmaps/yellow.xpm"
  76. Fl_Pixmap yellow_pixmap(yellow_xpm);
  77. #include "pixmaps/yellow_bomb.xpm"
  78. Fl_Pixmap yellow_bomb_pixmap(yellow_bomb_xpm);
  79. #include "pixmaps/cyan.xpm"
  80. Fl_Pixmap cyan_pixmap(cyan_xpm);
  81. #include "pixmaps/cyan_bomb.xpm"
  82. Fl_Pixmap cyan_bomb_pixmap(cyan_bomb_xpm);
  83. #include "pixmaps/magenta.xpm"
  84. Fl_Pixmap magenta_pixmap(magenta_xpm);
  85. #include "pixmaps/magenta_bomb.xpm"
  86. Fl_Pixmap magenta_bomb_pixmap(magenta_bomb_xpm);
  87. #include "pixmaps/gray.xpm"
  88. Fl_Pixmap gray_pixmap(gray_xpm);
  89. #include "pixmaps/gray_bomb.xpm"
  90. Fl_Pixmap gray_bomb_pixmap(gray_bomb_xpm);
  91. Fl_Pixmap *normal_pixmaps[] = {
  92. &red_pixmap,
  93. &green_pixmap,
  94. &blue_pixmap,
  95. &yellow_pixmap,
  96. &cyan_pixmap,
  97. &magenta_pixmap,
  98. &gray_pixmap
  99. };
  100. Fl_Pixmap *bomb_pixmaps[] = {
  101. &red_bomb_pixmap,
  102. &green_bomb_pixmap,
  103. &blue_bomb_pixmap,
  104. &yellow_bomb_pixmap,
  105. &cyan_bomb_pixmap,
  106. &magenta_bomb_pixmap,
  107. &gray_bomb_pixmap
  108. };
  109. const unsigned char screen_bits[] = {
  110. 0xff, 0x55, 0xff, 0xaa, 0xff, 0x55, 0xff, 0xaa
  111. };
  112. Fl_Bitmap screen_bitmap(screen_bits, 8, 8);
  113. Fl_Tiled_Image screen_tile(&screen_bitmap);
  114. // Sound class...
  115. //
  116. // There are MANY ways to implement sound in a FLTK application.
  117. // The approach we are using here is to conditionally compile OS-
  118. // specific code into the application - CoreAudio for MacOS X, the
  119. // standard Win32 API stuff for Windows, ALSA or X11 for Linux, and
  120. // X11 for all others. We have to support ALSA on Linux because the
  121. // current Xorg releases no longer support XBell() or the PC speaker.
  122. //
  123. // There are several good cross-platform audio libraries we could also
  124. // use, such as OpenAL, PortAudio, and SDL, however they were not chosen
  125. // for this application because of our limited use of sound.
  126. //
  127. // Many thanks to Ian MacArthur who provided sample code that led to
  128. // the CoreAudio implementation you see here!
  129. class BlockSound {
  130. // Private, OS-specific data...
  131. #ifdef __APPLE__
  132. AudioDeviceID device;
  133. # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  134. AudioDeviceIOProcID audio_proc_id;
  135. # endif
  136. AudioStreamBasicDescription format;
  137. short *data;
  138. int remaining;
  139. static OSStatus audio_cb(AudioDeviceID device,
  140. const AudioTimeStamp *current_time,
  141. const AudioBufferList *data_in,
  142. const AudioTimeStamp *time_in,
  143. AudioBufferList *data_out,
  144. const AudioTimeStamp *time_out,
  145. void *client_data);
  146. #elif defined(WIN32)
  147. HWAVEOUT device;
  148. HGLOBAL header_handle;
  149. LPWAVEHDR header_ptr;
  150. HGLOBAL data_handle;
  151. LPSTR data_ptr;
  152. #else
  153. # ifdef HAVE_ALSA_ASOUNDLIB_H
  154. snd_pcm_t *handle;
  155. # endif // HAVE_ALSA_ASOUNDLIB_H
  156. #endif // __APPLE__
  157. public:
  158. // Common data...
  159. static short *sample_data;
  160. static int sample_size;
  161. BlockSound();
  162. ~BlockSound();
  163. void play_explosion(float duration);
  164. };
  165. // Sound class globals...
  166. short *BlockSound::sample_data = NULL;
  167. int BlockSound::sample_size = 0;
  168. // Initialize the BlockSound class
  169. BlockSound::BlockSound() {
  170. sample_size = 0;
  171. #ifdef __APPLE__
  172. remaining = 0;
  173. UInt32 size = sizeof(device);
  174. if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
  175. &size, (void *)&device) != noErr) return;
  176. size = sizeof(format);
  177. if (AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyStreamFormat,
  178. &size, &format) != noErr) return;
  179. // Set up a format we like...
  180. format.mSampleRate = 44100.0; // 44.1kHz
  181. format.mChannelsPerFrame = 2; // stereo
  182. if (AudioDeviceSetProperty(device, NULL, 0, false,
  183. kAudioDevicePropertyStreamFormat,
  184. sizeof(format), &format) != noErr) return;
  185. // Check we got linear pcm - what to do if we did not ???
  186. if (format.mFormatID != kAudioFormatLinearPCM) return;
  187. // Attach the callback and start the device
  188. # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  189. if (AudioDeviceCreateIOProcID(device, audio_cb, (void *)this, &audio_proc_id) != noErr) return;
  190. AudioDeviceStart(device, audio_proc_id);
  191. # else
  192. if (AudioDeviceAddIOProc(device, audio_cb, (void *)this) != noErr) return;
  193. AudioDeviceStart(device, audio_cb);
  194. # endif
  195. sample_size = (int)format.mSampleRate;
  196. #elif defined(WIN32)
  197. WAVEFORMATEX format;
  198. memset(&format, 0, sizeof(format));
  199. format.cbSize = sizeof(format);
  200. format.wFormatTag = WAVE_FORMAT_PCM;
  201. format.nChannels = 2;
  202. format.nSamplesPerSec = 44100;
  203. format.nAvgBytesPerSec = 44100 * 4;
  204. format.nBlockAlign = 4;
  205. format.wBitsPerSample = 16;
  206. data_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, format.nSamplesPerSec * 4);
  207. if (!data_handle) return;
  208. data_ptr = (LPSTR)GlobalLock(data_handle);
  209. header_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
  210. if (!header_handle) return;
  211. header_ptr = (WAVEHDR *)GlobalLock(header_handle);
  212. header_ptr->lpData = data_ptr;
  213. header_ptr->dwFlags = 0;
  214. header_ptr->dwLoops = 0;
  215. if (waveOutOpen(&device, WAVE_MAPPER, &format, 0, 0, WAVE_ALLOWSYNC)
  216. != MMSYSERR_NOERROR) return;
  217. sample_size = format.nSamplesPerSec;
  218. #else
  219. # ifdef HAVE_ALSA_ASOUNDLIB_H
  220. handle = NULL;
  221. if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) >= 0) {
  222. // Initialize PCM sound stuff...
  223. snd_pcm_hw_params_t *params;
  224. snd_pcm_hw_params_alloca(&params);
  225. snd_pcm_hw_params_any(handle, params);
  226. snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
  227. snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16);
  228. snd_pcm_hw_params_set_channels(handle, params, 2);
  229. unsigned rate = 44100;
  230. int dir;
  231. snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
  232. snd_pcm_uframes_t period = (int)rate;
  233. snd_pcm_hw_params_set_period_size_near(handle, params, &period, &dir);
  234. sample_size = rate;
  235. if (snd_pcm_hw_params(handle, params) < 0) {
  236. sample_size = 0;
  237. snd_pcm_close(handle);
  238. handle = NULL;
  239. }
  240. }
  241. # endif // HAVE_ALSA_ASOUNDLIB_H
  242. #endif // __APPLE__
  243. if (sample_size) {
  244. // Make an explosion sound by passing white noise through a low pass
  245. // filter with a decreasing frequency...
  246. sample_data = new short[2 * sample_size];
  247. short *sample_ptr = sample_data;
  248. int max_sample = 2 * sample_size - 2;
  249. *sample_ptr++ = 0;
  250. *sample_ptr++ = 0;
  251. for (int j = max_sample; j > 0; j --, sample_ptr ++) {
  252. float freq = (float)j / (float)max_sample;
  253. float volume = 32767.0 * (0.5 * sqrt(freq) + 0.5);
  254. float sample = 0.0001 * ((rand() % 20001) - 10000);
  255. *sample_ptr = (int)(volume * freq * sample +
  256. (1.0 - freq) * sample_ptr[-2]);
  257. }
  258. }
  259. }
  260. // Cleanup the BlockSound class
  261. BlockSound::~BlockSound() {
  262. #ifdef __APPLE__
  263. if (sample_size) {
  264. # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  265. AudioDeviceStop(device, audio_proc_id);
  266. AudioDeviceDestroyIOProcID(device, audio_proc_id);
  267. # else
  268. AudioDeviceStop(device, audio_cb);
  269. AudioDeviceRemoveIOProc(device, audio_cb);
  270. # endif
  271. }
  272. #elif defined(WIN32)
  273. if (sample_size) {
  274. waveOutClose(device);
  275. GlobalUnlock(header_handle);
  276. GlobalFree(header_handle);
  277. GlobalUnlock(data_handle);
  278. GlobalFree(data_handle);
  279. }
  280. #else
  281. # ifdef HAVE_ALSA_ASOUNDLIB_H
  282. if (handle) {
  283. snd_pcm_drain(handle);
  284. snd_pcm_close(handle);
  285. }
  286. # endif // HAVE_ALSA_ASOUNDLIB_H
  287. #endif // __APPLE__
  288. if (sample_size) {
  289. delete[] sample_data;
  290. }
  291. }
  292. #ifdef __APPLE__
  293. // Callback function for writing audio data...
  294. OSStatus
  295. BlockSound::audio_cb(AudioDeviceID device,
  296. const AudioTimeStamp *current_time,
  297. const AudioBufferList *data_in,
  298. const AudioTimeStamp *time_in,
  299. AudioBufferList *data_out,
  300. const AudioTimeStamp *time_out,
  301. void *client_data) {
  302. BlockSound *ss = (BlockSound *)client_data;
  303. int count;
  304. float *buffer;
  305. if (!ss->remaining) return noErr;
  306. for (count = data_out->mBuffers[0].mDataByteSize / sizeof(float),
  307. buffer = (float*) data_out->mBuffers[0].mData;
  308. ss->remaining > 0 && count > 0;
  309. count --, ss->data ++, ss->remaining --) {
  310. *buffer++ = *(ss->data) / 32767.0;
  311. }
  312. while (count > 0) {
  313. *buffer++ = 0.0;
  314. count --;
  315. }
  316. return noErr;
  317. }
  318. #endif // __APPLE__
  319. // Play a note for the given amount of time...
  320. void
  321. BlockSound::play_explosion(float duration) {
  322. Fl::check();
  323. if (duration <= 0.0)
  324. return;
  325. #if defined(__APPLE__) || defined(WIN32) || defined(HAVE_ALSA_ASOUNDLIB_H)
  326. if (duration > 1.0)
  327. duration = 1.0;
  328. int samples = (int)(duration * sample_size);
  329. short *sample_ptr = sample_data + 2 * (sample_size - samples);
  330. #endif // __APPLE__ || WIN32 || HAVE_ALSA_ASOUNDLIB_H
  331. #ifdef __APPLE__
  332. // Point to the next note...
  333. data = sample_ptr;
  334. remaining = samples * 2;
  335. #elif defined(WIN32)
  336. if (sample_size) {
  337. memcpy(data_ptr, sample_ptr, samples * 4);
  338. header_ptr->dwBufferLength = samples * 4;
  339. waveOutPrepareHeader(device, header_ptr, sizeof(WAVEHDR));
  340. waveOutWrite(device, header_ptr, sizeof(WAVEHDR));
  341. } else Beep(440, (int)(1000.0 * duration));
  342. #elif defined(HAVE_ALSA_ASOUNDLIB_H)
  343. if (handle) {
  344. // Use ALSA to play the sound...
  345. if (snd_pcm_writei(handle, sample_ptr, samples) < 0) {
  346. snd_pcm_prepare(handle);
  347. snd_pcm_writei(handle, sample_ptr, samples);
  348. }
  349. return;
  350. }
  351. #endif // __APPLE__
  352. }
  353. class BlockWindow : public Fl_Double_Window
  354. {
  355. public:
  356. struct Block
  357. {
  358. int color;
  359. bool bomb;
  360. int y;
  361. };
  362. struct Column
  363. {
  364. int num_blocks;
  365. Block blocks[BLOCK_ROWS];
  366. int x;
  367. };
  368. private:
  369. Fl_Button *help_button_,
  370. *play_button_;
  371. int num_columns_;
  372. Column columns_[BLOCK_COLS];
  373. int count_;
  374. bool help_;
  375. int high_score_;
  376. float interval_;
  377. int level_;
  378. int num_colors_;
  379. int opened_columns_;
  380. bool paused_;
  381. static Fl_Preferences prefs_;
  382. int score_;
  383. BlockSound *sound_;
  384. char title_[255];
  385. int title_y_;
  386. void _BlockWindow();
  387. int bomb(int color);
  388. int click(int col, int row);
  389. static void help_cb(Fl_Widget *wi, BlockWindow *bw);
  390. void init();
  391. static void play_cb(Fl_Widget *wi, BlockWindow *bw);
  392. static void timeout_cb(BlockWindow *bw);
  393. public:
  394. BlockWindow(int X, int Y, int W, int H, const char *L = 0);
  395. BlockWindow(int W, int H, const char *L = 0);
  396. ~BlockWindow();
  397. void draw();
  398. int handle(int event);
  399. void new_game();
  400. int score() { return (score_); }
  401. void up_level();
  402. };
  403. Fl_Preferences BlockWindow::prefs_(Fl_Preferences::USER, "fltk.org", "blocks");
  404. int
  405. main(int argc, char *argv[]) {
  406. Fl::scheme("plastic");
  407. Fl::visible_focus(0);
  408. BlockWindow *bw = new BlockWindow(BLOCK_COLS * BLOCK_SIZE,
  409. BLOCK_ROWS * BLOCK_SIZE + 20,
  410. "Block Attack!");
  411. bw->show(argc, argv);
  412. return (Fl::run());
  413. }
  414. // Create a block window at the specified position
  415. BlockWindow::BlockWindow(int X, int Y, int W, int H, const char *L)
  416. : Fl_Double_Window(X, Y, W, H, L) {
  417. _BlockWindow();
  418. }
  419. // Create a block window
  420. BlockWindow::BlockWindow(int W, int H, const char *L)
  421. : Fl_Double_Window(W, H, L) {
  422. _BlockWindow();
  423. }
  424. // Delete a block window
  425. BlockWindow::~BlockWindow() {
  426. Fl::remove_timeout((Fl_Timeout_Handler)timeout_cb, (void *)this);
  427. }
  428. // Initialize a block window...
  429. void
  430. BlockWindow::_BlockWindow() {
  431. init();
  432. help_button_ = new Fl_Button(0, 0, 20, 20, "?");
  433. help_button_->callback((Fl_Callback *)help_cb, this);
  434. help_button_->shortcut('?');
  435. play_button_ = new Fl_Button(80, (h() - 80) / 2, 80, 80, "@>");
  436. play_button_->callback((Fl_Callback *)play_cb, this);
  437. play_button_->labelsize(44);
  438. play_button_->shortcut(' ');
  439. sound_ = new BlockSound();
  440. prefs_.get("high_score", high_score_, 0);
  441. Fl::add_timeout(0.1, (Fl_Timeout_Handler)timeout_cb, (void *)this);
  442. }
  443. // Bomb all blocks of a given color and return the number of affected blocks
  444. int
  445. BlockWindow::bomb(int color) {
  446. int j, k;
  447. int count;
  448. Block *b;
  449. Column *c;
  450. if (color >= BLOCK_BLAST) return (0);
  451. for (j = num_columns_, c = columns_, count = 1; j > 0; j --, c ++)
  452. for (k = c->num_blocks, b = c->blocks; k > 0; k --, b ++)
  453. if (b->color == color) {
  454. b->color = -color;
  455. count ++;
  456. }
  457. return (count);
  458. }
  459. // Tag all blocks connected to the clicked block and return the number
  460. // of affected blocks
  461. int
  462. BlockWindow::click(int col, int row) {
  463. Block *b;
  464. Column *c;
  465. int count, color;
  466. c = columns_ + col;
  467. b = c->blocks + row;
  468. color = b->color;
  469. if (color < 0 || color >= BLOCK_BLAST) return (0);
  470. // Find the bottom block...
  471. while (row > 0 && b[-1].color == color) {
  472. row --;
  473. b --;
  474. }
  475. count = 0;
  476. while (row < c->num_blocks && b->color == color) {
  477. b->color = -color;
  478. if (col > 0 && row < c[-1].num_blocks &&
  479. c[-1].blocks[row].color == color) {
  480. count += click(col - 1, row);
  481. }
  482. if (col < (num_columns_ - 1) && row < c[1].num_blocks &&
  483. c[1].blocks[row].color == color) {
  484. count += click(col + 1, row);
  485. }
  486. count ++;
  487. row ++;
  488. b ++;
  489. }
  490. return (count);
  491. }
  492. // Draw the block window...
  493. void
  494. BlockWindow::draw() {
  495. int j, k, xx, yy;
  496. Block *b;
  497. Column *c;
  498. // Draw the blocks...
  499. fl_color(FL_BLACK);
  500. fl_rectf(0, 0, w(), h());
  501. // Draw the blocks...
  502. for (j = num_columns_, c = columns_; j > 0; j --, c ++)
  503. for (k = c->num_blocks, b = c->blocks; k > 0; k --, b ++) {
  504. xx = w() - c->x;
  505. yy = h() - BLOCK_SIZE - b->y;
  506. if (b->color >= BLOCK_BLAST) {
  507. b->color ++;
  508. blast_pixmap.draw(xx, yy);
  509. } else if (b->color < 0) {
  510. if (b->bomb) bomb_pixmaps[-b->color - 1]->draw(xx, yy);
  511. else normal_pixmaps[-b->color - 1]->draw(xx, yy);
  512. } else {
  513. if (b->bomb) bomb_pixmaps[b->color - 1]->draw(xx, yy);
  514. else normal_pixmaps[b->color - 1]->draw(xx, yy);
  515. }
  516. }
  517. if (interval_ < 0.0 || paused_) {
  518. fl_color(FL_BLACK);
  519. screen_tile.draw(0, 0, w(), h(), 0, 0);
  520. }
  521. // Redraw the widgets...
  522. play_button_->redraw();
  523. help_button_->redraw();
  524. draw_children();
  525. // Draw any paused/game over/new game message...
  526. if ((paused_ || interval_ < 0.0) && play_button_->w() == 80) {
  527. const char *s;
  528. if (help_) {
  529. s = "Click on adjacent blocks of the same color. Clear all blocks "
  530. "before they reach the left side.";
  531. fl_font(FL_HELVETICA_BOLD, 24);
  532. fl_color(FL_BLACK);
  533. fl_draw(s, 171, 3, w() - 250, h() - 6,
  534. (Fl_Align)(FL_ALIGN_WRAP | FL_ALIGN_LEFT));
  535. fl_color(FL_YELLOW);
  536. fl_draw(s, 168, 0, w() - 250, h(),
  537. (Fl_Align)(FL_ALIGN_WRAP | FL_ALIGN_LEFT));
  538. } else {
  539. if (interval_ < 0.0) {
  540. #ifdef DEBUG
  541. // Show sample waveform...
  542. short *sample_ptr;
  543. for (i = 0; i < 2; i ++)
  544. {
  545. fl_color(FL_RED + i);
  546. fl_begin_line();
  547. for (j = 0, sample_ptr = sound_->sample_data + i;
  548. j < sound_->sample_size;
  549. j ++, sample_ptr += 2)
  550. fl_vertex(j * w() / sound_->sample_size,
  551. *sample_ptr * h() / 4 / 65534 + h() / 2);
  552. fl_end_line();
  553. }
  554. #endif // DEBUG
  555. if (num_columns_ && (time(NULL) & 7) < 4) s = "Game Over";
  556. else s = "Block Attack!\nby Michael R Sweet";
  557. } else s = "Paused";
  558. fl_font(FL_HELVETICA_BOLD, 32);
  559. fl_color(FL_BLACK);
  560. fl_draw(s, 6, 6, w() - 6, h() - 6, FL_ALIGN_CENTER);
  561. fl_color(FL_YELLOW);
  562. fl_draw(s, 0, 0, w(), h(), FL_ALIGN_CENTER);
  563. }
  564. }
  565. // Draw the scores and level...
  566. char s[255];
  567. sprintf(s, " Score: %d", score_);
  568. fl_color(FL_WHITE);
  569. fl_font(FL_HELVETICA, 14);
  570. fl_draw(s, 40, 0, w() - 40, 20, FL_ALIGN_LEFT);
  571. sprintf(s, "High Score: %d ", high_score_);
  572. fl_draw(s, 0, 0, w(), 20, FL_ALIGN_RIGHT);
  573. if (level_ > 1 || title_y_ <= 0)
  574. {
  575. sprintf(s, "Level: %d ", level_);
  576. fl_draw(s, 0, 0, w(), 20, FL_ALIGN_CENTER);
  577. }
  578. if (title_y_ > 0 && interval_ > 0.0)
  579. {
  580. int sz = 14 + title_y_ * 86 / h();
  581. fl_font(FL_HELVETICA_BOLD, sz);
  582. fl_color(FL_YELLOW);
  583. fl_draw(title_, 0, title_y_, w(), sz, FL_ALIGN_CENTER);
  584. }
  585. }
  586. // Handle mouse clicks, etc.
  587. int
  588. BlockWindow::handle(int event) {
  589. int j, k, mx, my, count;
  590. Block *b;
  591. Column *c;
  592. if (Fl_Double_Window::handle(event)) return (1);
  593. else if (interval_ < 0.0 || paused_) return (0);
  594. switch (event) {
  595. case FL_KEYBOARD:
  596. if (Fl::event_text()) {
  597. if (strcmp(Fl::event_text(), "+") == 0)
  598. up_level();
  599. }
  600. break;
  601. case FL_PUSH :
  602. mx = w() - Fl::event_x() + BLOCK_SIZE;
  603. my = h() - Fl::event_y();
  604. count = 0;
  605. b = 0;
  606. for (j = 0, c = columns_; !count && j < num_columns_; j ++, c ++)
  607. for (k = 0, b = c->blocks; !count && k < c->num_blocks; k ++, b ++)
  608. if (mx >= c->x && mx < (c->x + BLOCK_SIZE) &&
  609. my >= b->y && my < (b->y + BLOCK_SIZE)) {
  610. if (b->bomb) count = bomb(b->color);
  611. else count = click(j, k);
  612. break;
  613. }
  614. if (count < 2) {
  615. for (j = 0, c = columns_; j < num_columns_; j ++, c ++)
  616. for (k = 0, b = c->blocks; k < c->num_blocks; k ++, b ++)
  617. if (b->color < 0) b->color = -b->color;
  618. } else {
  619. count --;
  620. if (b->bomb) {
  621. sound_->play_explosion(0.19 + 0.005 * count);
  622. interval_ *= 0.995;
  623. score_ += count;
  624. } else {
  625. sound_->play_explosion(0.09 + 0.005 * count);
  626. interval_ *= 0.999;
  627. score_ += count * count;
  628. }
  629. if (score_ > high_score_) {
  630. high_score_ = score_;
  631. prefs_.set("high_score", high_score_);
  632. }
  633. for (j = 0, c = columns_; j < num_columns_; j ++, c ++)
  634. for (k = 0, b = c->blocks; k < c->num_blocks; k ++, b ++)
  635. if (b->color < 0) b->color = BLOCK_BLAST;
  636. }
  637. return (1);
  638. }
  639. return (0);
  640. }
  641. // Toggle the on-line help...
  642. void
  643. BlockWindow::help_cb(Fl_Widget *, BlockWindow *bw) {
  644. bw->paused_ = bw->help_ = !bw->help_;
  645. bw->play_button_->label("@>");
  646. bw->redraw();
  647. }
  648. // Initialize the block window...
  649. void
  650. BlockWindow::init() {
  651. count_ = 0;
  652. help_ = false;
  653. interval_ = -1.0;
  654. level_ = 1;
  655. num_colors_ = 3;
  656. num_columns_ = 0;
  657. paused_ = false;
  658. score_ = 0;
  659. title_[0] = '\0';
  660. title_y_ = 0;
  661. }
  662. // Start a new game...
  663. void
  664. BlockWindow::new_game() {
  665. // Seed the random number generator...
  666. srand(time(NULL));
  667. init();
  668. interval_ = 0.1;
  669. opened_columns_ = 0;
  670. strcpy(title_, "Level: 1");
  671. title_y_ = h();
  672. redraw();
  673. }
  674. // Play/pause...
  675. void
  676. BlockWindow::play_cb(Fl_Widget *wi, BlockWindow *bw) {
  677. if (bw->interval_ < 0) bw->new_game();
  678. else bw->paused_ = !bw->paused_;
  679. if (bw->paused_) wi->label("@>");
  680. else {
  681. wi->label("@-2||");
  682. bw->help_ = false;
  683. }
  684. }
  685. void BlockWindow::up_level() {
  686. interval_ *= 0.95;
  687. opened_columns_ = 0;
  688. if (num_colors_ < 7) num_colors_ ++;
  689. level_ ++;
  690. sprintf(title_, "Level: %d", level_);
  691. title_y_ = h();
  692. Fl::repeat_timeout(interval_, (Fl_Timeout_Handler)timeout_cb, (void *)this);
  693. }
  694. // Animate the game...
  695. void
  696. BlockWindow::timeout_cb(BlockWindow *bw) {
  697. int i, j;
  698. Block *b;
  699. Column *c;
  700. int lastx, lasty;
  701. #ifdef DEBUG
  702. struct timeval curtime;
  703. static struct timeval lasttime;
  704. gettimeofday(&curtime, NULL);
  705. printf("%.3f (%+f - %f)\n",
  706. curtime.tv_sec + 0.000001 * curtime.tv_usec,
  707. curtime.tv_sec - lasttime.tv_sec +
  708. 0.000001 * (curtime.tv_usec - lasttime.tv_usec), bw->interval_);
  709. lasttime = curtime;
  710. #endif // DEBUG
  711. // Update blocks that have been destroyed...
  712. for (i = 0, c = bw->columns_; i < bw->num_columns_; i ++, c ++)
  713. for (j = 0, b = c->blocks; j < c->num_blocks; j ++, b ++)
  714. if (b->color > (BLOCK_BLAST + 1)) {
  715. bw->redraw();
  716. c->num_blocks --;
  717. if (j < c->num_blocks) {
  718. memmove(b, b + 1, (c->num_blocks - j) * sizeof(Block));
  719. }
  720. j --;
  721. b --;
  722. if (!c->num_blocks) {
  723. bw->num_columns_ --;
  724. if (i < bw->num_columns_) {
  725. memmove(c, c + 1, (bw->num_columns_ - i) * sizeof(Column));
  726. }
  727. i --;
  728. c --;
  729. j = c->num_blocks;
  730. }
  731. }
  732. // Let the rest of the blocks fall and/or move...
  733. for (i = bw->num_columns_, c = bw->columns_, lastx = c->x;
  734. i > 0;
  735. i --, c ++) {
  736. if (c->x > lastx) {
  737. c->x -= 8;
  738. bw->redraw();
  739. }
  740. lastx = c->x + BLOCK_SIZE;
  741. if (!bw->paused_ && bw->interval_ > 0.0) {
  742. bw->redraw();
  743. c->x ++;
  744. }
  745. for (j = c->num_blocks, b = c->blocks, lasty = 0; j > 0; j --, b ++) {
  746. if (b->y > lasty) {
  747. bw->redraw();
  748. b->y -= 8;
  749. }
  750. lasty = b->y + BLOCK_SIZE;
  751. }
  752. }
  753. // Slide the title text as needed...
  754. if (bw->title_y_ > 0) {
  755. bw->redraw();
  756. bw->title_y_ -= 5;
  757. }
  758. // Play the game...
  759. if (!bw->paused_ && bw->interval_ > 0.0) {
  760. bw->count_ --;
  761. if (bw->count_ <= 0) {
  762. bw->redraw();
  763. bw->count_ = BLOCK_SIZE;
  764. if (bw->num_columns_ == BLOCK_COLS) {
  765. bw->interval_ = -1.0;
  766. bw->sound_->play_explosion(0.8);
  767. bw->play_button_->label("@>");
  768. } else {
  769. bw->opened_columns_ ++;
  770. if (bw->opened_columns_ > (2 * BLOCK_COLS)) {
  771. bw->up_level();
  772. }
  773. c = bw->columns_;
  774. if (bw->num_columns_) {
  775. memmove(c + 1, c, bw->num_columns_ * sizeof(Column));
  776. }
  777. bw->num_columns_ ++;
  778. c->x = 0;
  779. c->num_blocks = BLOCK_ROWS;
  780. for (j = 0, b = c->blocks; j < BLOCK_ROWS; j ++, b ++) {
  781. b->bomb = bw->num_colors_ > 3 && (rand() & 127) < bw->num_colors_;
  782. b->color = 1 + (rand() % bw->num_colors_);
  783. b->y = j * (BLOCK_SIZE + 8) + 24;
  784. }
  785. }
  786. }
  787. }
  788. else
  789. {
  790. bw->count_ --;
  791. if (bw->count_ <= 0) {
  792. bw->count_ = 40;
  793. bw->redraw();
  794. }
  795. }
  796. // Update the play/pause button as needed...
  797. if ((bw->paused_ || bw->interval_< 0.0) &&
  798. bw->play_button_->w() < 80) {
  799. int s = bw->play_button_->w() + 10;
  800. bw->play_button_->resize(s, (s - 20) * (bw->h() - s) / 120, s, s);
  801. bw->play_button_->labelsize(s / 2 + 4);
  802. bw->redraw();
  803. } else if ((!bw->paused_ && bw->interval_ > 0.0) &&
  804. bw->play_button_->w() > 20) {
  805. int s = bw->play_button_->w() - 5;
  806. bw->play_button_->resize(s, (s - 20) * (bw->h() - s) / 120, s, s);
  807. bw->play_button_->labelsize(s / 2 + 4);
  808. bw->redraw();
  809. }
  810. if (bw->interval_ > 0.0) {
  811. Fl::repeat_timeout(bw->interval_, (Fl_Timeout_Handler)timeout_cb,
  812. (void *)bw);
  813. } else {
  814. Fl::repeat_timeout(0.1, (Fl_Timeout_Handler)timeout_cb,
  815. (void *)bw);
  816. }
  817. }
  818. //
  819. // End of "$Id: blocks.cxx 7904 2010-11-28 21:12:59Z matt $".
  820. //