source: rtems-graphics-toolkit/fltk-1.3.0/test/blocks.cxx @ 0904354

Last change on this file since 0904354 was 0904354, checked in by Alexandru-Sever Horin <alex.sever.h@…>, on 07/11/12 at 00:33:29

Some test examples. Probably others work too

  • Property mode set to 100644
File size: 24.2 KB
Line 
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
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>
42
43// Audio headers...
44#include <config.h>
45
46#ifndef WIN32
47#  include <unistd.h>
48#  include <sys/time.h>
49#endif // !WIN32
50
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
61
62
63#define BLOCK_COLS      20
64#define BLOCK_ROWS      10
65#define BLOCK_SIZE      32
66#define BLOCK_BLAST     100
67
68#include "pixmaps/blast.xpm"
69Fl_Pixmap blast_pixmap(blast_xpm);
70
71#include "pixmaps/red.xpm"
72Fl_Pixmap red_pixmap(red_xpm);
73#include "pixmaps/red_bomb.xpm"
74Fl_Pixmap red_bomb_pixmap(red_bomb_xpm);
75
76#include "pixmaps/green.xpm"
77Fl_Pixmap green_pixmap(green_xpm);
78#include "pixmaps/green_bomb.xpm"
79Fl_Pixmap green_bomb_pixmap(green_bomb_xpm);
80
81#include "pixmaps/blue.xpm"
82Fl_Pixmap blue_pixmap(blue_xpm);
83#include "pixmaps/blue_bomb.xpm"
84Fl_Pixmap blue_bomb_pixmap(blue_bomb_xpm);
85
86#include "pixmaps/yellow.xpm"
87Fl_Pixmap yellow_pixmap(yellow_xpm);
88#include "pixmaps/yellow_bomb.xpm"
89Fl_Pixmap yellow_bomb_pixmap(yellow_bomb_xpm);
90
91#include "pixmaps/cyan.xpm"
92Fl_Pixmap cyan_pixmap(cyan_xpm);
93#include "pixmaps/cyan_bomb.xpm"
94Fl_Pixmap cyan_bomb_pixmap(cyan_bomb_xpm);
95
96#include "pixmaps/magenta.xpm"
97Fl_Pixmap magenta_pixmap(magenta_xpm);
98#include "pixmaps/magenta_bomb.xpm"
99Fl_Pixmap magenta_bomb_pixmap(magenta_bomb_xpm);
100
101#include "pixmaps/gray.xpm"
102Fl_Pixmap gray_pixmap(gray_xpm);
103#include "pixmaps/gray_bomb.xpm"
104Fl_Pixmap gray_bomb_pixmap(gray_bomb_xpm);
105
106Fl_Pixmap *normal_pixmaps[] = {
107  &red_pixmap,
108  &green_pixmap,
109  &blue_pixmap,
110  &yellow_pixmap,
111  &cyan_pixmap,
112  &magenta_pixmap,
113  &gray_pixmap
114};
115Fl_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
123};
124
125const unsigned char screen_bits[] = {
126  0xff, 0x55, 0xff, 0xaa, 0xff, 0x55, 0xff, 0xaa
127};
128Fl_Bitmap screen_bitmap(screen_bits, 8, 8);
129Fl_Tiled_Image screen_tile(&screen_bitmap);
130
131
132// Sound class...
133//
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.
140//
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.
144//
145// Many thanks to Ian MacArthur who provided sample code that led to
146// the CoreAudio implementation you see here!
147class 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;
157
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;
171
172#else
173#  ifdef HAVE_ALSA_ASOUNDLIB_H
174  snd_pcm_t *handle;
175#  endif // HAVE_ALSA_ASOUNDLIB_H
176#endif // __APPLE__
177
178  public:
179
180  // Common data...
181  static short *sample_data;
182  static int sample_size;
183
184  BlockSound();
185  ~BlockSound();
186
187  void  play_explosion(float duration);
188};
189
190// Sound class globals...
191short *BlockSound::sample_data = NULL;
192int BlockSound::sample_size = 0;
193
194
195// Initialize the BlockSound class
196BlockSound::BlockSound() {
197  sample_size = 0;
198
199#ifdef __APPLE__
200  remaining = 0;
201
202  UInt32 size = sizeof(device);
203
204  if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
205                               &size, (void *)&device) != noErr) return;
206
207  size = sizeof(format);
208  if (AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyStreamFormat,
209                             &size, &format) != noErr) return;
210
211  // Set up a format we like...
212  format.mSampleRate       = 44100.0;   // 44.1kHz
213  format.mChannelsPerFrame = 2;         // stereo
214
215  if (AudioDeviceSetProperty(device, NULL, 0, false,
216                             kAudioDevicePropertyStreamFormat,
217                             sizeof(format), &format) != noErr) return;
218
219  // Check we got linear pcm - what to do if we did not ???
220  if (format.mFormatID != kAudioFormatLinearPCM) return;
221
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
230
231  sample_size = (int)format.mSampleRate;
232
233#elif defined(WIN32)
234  WAVEFORMATEX  format;
235
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;
244
245  data_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, format.nSamplesPerSec * 4);
246  if (!data_handle) return;
247
248  data_ptr = (LPSTR)GlobalLock(data_handle);
249
250  header_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
251  if (!header_handle) return;
252
253  header_ptr = (WAVEHDR *)GlobalLock(header_handle);
254
255  header_ptr->lpData  = data_ptr;
256  header_ptr->dwFlags = 0;
257  header_ptr->dwLoops = 0;
258
259  if (waveOutOpen(&device, WAVE_MAPPER, &format, 0, 0, WAVE_ALLOWSYNC)
260          != MMSYSERR_NOERROR) return;
261
262  sample_size = format.nSamplesPerSec;
263
264#else
265#  ifdef HAVE_ALSA_ASOUNDLIB_H
266  handle = NULL;
267
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;
271
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);
282
283    sample_size = rate;
284
285    if (snd_pcm_hw_params(handle, params) < 0) {
286      sample_size = 0;
287      snd_pcm_close(handle);
288      handle = NULL;
289    }
290  }
291#  endif // HAVE_ALSA_ASOUNDLIB_H
292#endif // __APPLE__
293
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];
298
299    short *sample_ptr = sample_data;
300    int max_sample = 2 * sample_size - 2;
301
302    *sample_ptr++ = 0;
303    *sample_ptr++ = 0;
304
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);
309
310      *sample_ptr = (int)(volume * freq * sample +
311                          (1.0 - freq) * sample_ptr[-2]);
312    }
313  }
314}
315
316
317// Cleanup the BlockSound class
318BlockSound::~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
328  }
329
330#elif defined(WIN32)
331  if (sample_size) {
332    waveOutClose(device);
333
334    GlobalUnlock(header_handle);
335    GlobalFree(header_handle);
336
337    GlobalUnlock(data_handle);
338    GlobalFree(data_handle);
339  }
340
341#else
342#  ifdef HAVE_ALSA_ASOUNDLIB_H
343  if (handle) {
344    snd_pcm_drain(handle);
345    snd_pcm_close(handle);
346  }
347#  endif // HAVE_ALSA_ASOUNDLIB_H
348#endif // __APPLE__
349
350  if (sample_size) {
351    delete[] sample_data;
352  }
353}
354
355
356#ifdef __APPLE__
357// Callback function for writing audio data...
358OSStatus
359BlockSound::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;
369
370  if (!ss->remaining) return noErr;
371
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;
377  }
378
379  while (count > 0) {
380    *buffer++ = 0.0;
381    count --;
382  }
383
384  return noErr;
385}
386#endif // __APPLE__
387
388
389// Play a note for the given amount of time...
390void
391BlockSound::play_explosion(float duration) {
392  Fl::check();
393
394  if (duration <= 0.0)
395    return;
396
397#if defined(__APPLE__) || defined(WIN32) || defined(HAVE_ALSA_ASOUNDLIB_H)
398  if (duration > 1.0)
399    duration = 1.0;
400
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
404
405#ifdef __APPLE__
406  // Point to the next note...
407  data      = sample_ptr;
408  remaining = samples * 2;
409
410#elif defined(WIN32)
411  if (sample_size) {
412    memcpy(data_ptr, sample_ptr, samples * 4);
413
414    header_ptr->dwBufferLength = samples * 4;
415    waveOutPrepareHeader(device, header_ptr, sizeof(WAVEHDR));
416
417    waveOutWrite(device, header_ptr, sizeof(WAVEHDR));
418  } else Beep(440, (int)(1000.0 * duration));
419
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);
426    }
427    return;
428  }
429#endif // __APPLE__
430}
431
432
433class BlockWindow : public Fl_Double_Window
434{
435  public:
436
437  struct Block
438  {
439    int         color;
440    bool        bomb;
441    int         y;
442  };
443
444  struct Column
445  {
446    int         num_blocks;
447    Block       blocks[BLOCK_ROWS];
448    int         x;
449  };
450
451  private:
452
453  Fl_Button     *help_button_,
454                *play_button_;
455
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_;
471
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);
479
480  public:
481
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();
485
486  void          draw();
487  int           handle(int event);
488  void          new_game();
489  int           score() { return (score_); }
490  void          up_level();
491};
492
493
494Fl_Preferences  BlockWindow::prefs_(Fl_Preferences::USER, "fltk.org", "blocks");
495
496
497extern "C" int rtems_main(int argc, char *argv[]) {
498  Fl::scheme("plastic");
499  Fl::visible_focus(0);
500
501  BlockWindow   *bw = new BlockWindow(BLOCK_COLS * BLOCK_SIZE,
502                                      BLOCK_ROWS * BLOCK_SIZE + 20,
503                                      "Block Attack!");
504
505  bw->show(argc, argv);
506
507  return (Fl::run());
508}
509
510
511// Create a block window at the specified position
512BlockWindow::BlockWindow(int X, int Y, int W, int H, const char *L)
513    : Fl_Double_Window(X, Y, W, H, L) {
514  _BlockWindow();
515}
516
517
518// Create a block window
519BlockWindow::BlockWindow(int W, int H, const char *L)
520    : Fl_Double_Window(W, H, L) {
521  _BlockWindow();
522}
523
524
525// Delete a block window
526BlockWindow::~BlockWindow() {
527  Fl::remove_timeout((Fl_Timeout_Handler)timeout_cb, (void *)this);
528}
529
530
531// Initialize a block window...
532void
533BlockWindow::_BlockWindow() {
534  init();
535
536  help_button_ = new Fl_Button(0, 0, 20, 20, "?");
537  help_button_->callback((Fl_Callback *)help_cb, this);
538  help_button_->shortcut('?');
539
540  play_button_ = new Fl_Button(80, (h() - 80) / 2, 80, 80, "@>");
541  play_button_->callback((Fl_Callback *)play_cb, this);
542  play_button_->labelsize(44);
543  play_button_->shortcut(' ');
544
545  sound_ = new BlockSound();
546
547  prefs_.get("high_score", high_score_, 0);
548
549  Fl::add_timeout(0.1, (Fl_Timeout_Handler)timeout_cb, (void *)this);
550}
551
552
553// Bomb all blocks of a given color and return the number of affected blocks
554int
555BlockWindow::bomb(int color) {
556  int           j, k;
557  int           count;
558  Block         *b;
559  Column        *c;
560
561
562  if (color >= BLOCK_BLAST) return (0);
563
564  for (j = num_columns_, c = columns_, count = 1; j > 0; j --, c ++)
565    for (k = c->num_blocks, b = c->blocks; k > 0; k --, b ++)
566      if (b->color == color) {
567        b->color = -color;
568        count ++;
569      }
570
571  return (count);
572}
573
574
575// Tag all blocks connected to the clicked block and return the number
576// of affected blocks
577int
578BlockWindow::click(int col, int row) {
579  Block         *b;
580  Column        *c;
581  int           count, color;
582
583
584  c     = columns_ + col;
585  b     = c->blocks + row;
586  color = b->color;
587
588  if (color < 0 || color >= BLOCK_BLAST) return (0);
589
590  // Find the bottom block...
591  while (row > 0 && b[-1].color == color) {
592    row --;
593    b --;
594  }
595
596  count = 0;
597
598  while (row < c->num_blocks && b->color == color) {
599    b->color = -color;
600
601    if (col > 0 && row < c[-1].num_blocks &&
602        c[-1].blocks[row].color == color) {
603      count += click(col - 1, row);
604    }
605
606    if (col < (num_columns_ - 1) && row < c[1].num_blocks &&
607        c[1].blocks[row].color == color) {
608      count += click(col + 1, row);
609    }
610
611    count ++;
612    row ++;
613    b ++;
614  }
615
616  return (count);
617}
618
619
620// Draw the block window...
621void
622BlockWindow::draw() {
623  int           j, k, xx, yy;
624  Block         *b;
625  Column        *c;
626
627
628  // Draw the blocks...
629  fl_color(FL_BLACK);
630  fl_rectf(0, 0, w(), h());
631
632  // Draw the blocks...
633  for (j = num_columns_, c = columns_; j > 0; j --, c ++)
634    for (k = c->num_blocks, b = c->blocks; k > 0; k --, b ++) {
635      xx = w() - c->x;
636      yy = h() - BLOCK_SIZE - b->y;
637
638      if (b->color >= BLOCK_BLAST) {
639        b->color ++;
640        blast_pixmap.draw(xx, yy);
641      } else if (b->color < 0) {
642        if (b->bomb) bomb_pixmaps[-b->color - 1]->draw(xx, yy);
643        else normal_pixmaps[-b->color - 1]->draw(xx, yy);
644      } else {
645        if (b->bomb) bomb_pixmaps[b->color - 1]->draw(xx, yy);
646        else normal_pixmaps[b->color - 1]->draw(xx, yy);
647      }
648    }
649
650  if (interval_ < 0.0 || paused_) {
651    fl_color(FL_BLACK);
652    screen_tile.draw(0, 0, w(), h(), 0, 0);
653  }
654
655  // Redraw the widgets...
656  play_button_->redraw();
657  help_button_->redraw();
658  draw_children();
659
660  // Draw any paused/game over/new game message...
661  if ((paused_ || interval_ < 0.0) && play_button_->w() == 80) {
662    const char *s;
663
664    if (help_) {
665      s = "Click on adjacent blocks of the same color. Clear all blocks "
666          "before they reach the left side.";
667
668      fl_font(FL_HELVETICA_BOLD, 24);
669      fl_color(FL_BLACK);
670      fl_draw(s, 171, 3, w() - 250, h() - 6,
671              (Fl_Align)(FL_ALIGN_WRAP | FL_ALIGN_LEFT));
672
673      fl_color(FL_YELLOW);
674      fl_draw(s, 168, 0, w() - 250, h(),
675              (Fl_Align)(FL_ALIGN_WRAP | FL_ALIGN_LEFT));
676    } else {
677      if (interval_ < 0.0) {
678#ifdef DEBUG
679        // Show sample waveform...
680        short *sample_ptr;
681
682        for (i = 0; i < 2; i ++)
683        {
684          fl_color(FL_RED + i);
685          fl_begin_line();
686          for (j = 0, sample_ptr = sound_->sample_data + i;
687               j < sound_->sample_size;
688               j ++, sample_ptr += 2)
689            fl_vertex(j * w() / sound_->sample_size,
690                      *sample_ptr * h() / 4 / 65534 + h() / 2);
691          fl_end_line();
692        }
693#endif // DEBUG
694
695        if (num_columns_ && (time(NULL) & 7) < 4) s = "Game Over";
696        else s = "Block Attack!\nby Michael R Sweet";
697      } else s = "Paused";
698
699      fl_font(FL_HELVETICA_BOLD, 32);
700      fl_color(FL_BLACK);
701      fl_draw(s, 6, 6, w() - 6, h() - 6, FL_ALIGN_CENTER);
702
703      fl_color(FL_YELLOW);
704      fl_draw(s, 0, 0, w(), h(), FL_ALIGN_CENTER);
705    }
706  }
707
708  // Draw the scores and level...
709  char s[255];
710
711  sprintf(s, " Score: %d", score_);
712  fl_color(FL_WHITE);
713  fl_font(FL_HELVETICA, 14);
714  fl_draw(s, 40, 0, w() - 40, 20, FL_ALIGN_LEFT);
715
716  sprintf(s, "High Score: %d ", high_score_);
717  fl_draw(s, 0, 0, w(), 20, FL_ALIGN_RIGHT);
718
719  if (level_ > 1 || title_y_ <= 0)
720  {
721    sprintf(s, "Level: %d ", level_);
722    fl_draw(s, 0, 0, w(), 20, FL_ALIGN_CENTER);
723  }
724
725  if (title_y_ > 0 && interval_ > 0.0)
726  {
727    int sz = 14 + title_y_ * 86 / h();
728
729    fl_font(FL_HELVETICA_BOLD, sz);
730    fl_color(FL_YELLOW);
731    fl_draw(title_, 0, title_y_, w(), sz, FL_ALIGN_CENTER);
732  }
733}
734
735
736// Handle mouse clicks, etc.
737int
738BlockWindow::handle(int event) {
739  int           j, k, mx, my, count;
740  Block         *b;
741  Column        *c;
742
743
744  if (Fl_Double_Window::handle(event)) return (1);
745  else if (interval_ < 0.0 || paused_) return (0);
746
747  switch (event) {
748    case FL_KEYBOARD:
749        if (Fl::event_text()) {
750          if (strcmp(Fl::event_text(), "+") == 0)
751            up_level();
752        }
753        break;
754    case FL_PUSH :
755        mx    = w() - Fl::event_x() + BLOCK_SIZE;
756        my    = h() - Fl::event_y();
757        count = 0;
758        b     = 0;
759
760        for (j = 0, c = columns_; !count && j < num_columns_; j ++, c ++)
761          for (k = 0, b = c->blocks; !count && k < c->num_blocks; k ++, b ++)
762            if (mx >= c->x && mx < (c->x + BLOCK_SIZE) &&
763                my >= b->y && my < (b->y + BLOCK_SIZE)) {
764              if (b->bomb) count = bomb(b->color);
765              else count = click(j, k);
766
767              break;
768            }
769
770        if (count < 2) {
771          for (j = 0, c = columns_; j < num_columns_; j ++, c ++)
772            for (k = 0, b = c->blocks; k < c->num_blocks; k ++, b ++)
773              if (b->color < 0) b->color = -b->color;
774        } else {
775          count --;
776
777          if (b->bomb) {
778            sound_->play_explosion(0.19 + 0.005 * count);
779
780            interval_ *= 0.995;
781            score_ += count;
782          } else {
783            sound_->play_explosion(0.09 + 0.005 * count);
784
785            interval_ *= 0.999;
786            score_ += count * count;
787          }
788
789          if (score_ > high_score_) {
790            high_score_ = score_;
791            prefs_.set("high_score", high_score_);
792          }
793
794          for (j = 0, c = columns_; j < num_columns_; j ++, c ++)
795            for (k = 0, b = c->blocks; k < c->num_blocks; k ++, b ++)
796              if (b->color < 0) b->color = BLOCK_BLAST;
797        }
798        return (1);
799  }
800
801  return (0);
802}
803
804
805// Toggle the on-line help...
806void
807BlockWindow::help_cb(Fl_Widget *, BlockWindow *bw) {
808  bw->paused_ = bw->help_ = !bw->help_;
809  bw->play_button_->label("@>");
810  bw->redraw();
811}
812
813
814// Initialize the block window...
815void
816BlockWindow::init() {
817  count_       = 0;
818  help_        = false;
819  interval_    = -1.0;
820  level_       = 1;
821  num_colors_  = 3;
822  num_columns_ = 0;
823  paused_      = false;
824  score_       = 0;
825  title_[0]    = '\0';
826  title_y_     = 0;
827}
828
829
830// Start a new game...
831void
832BlockWindow::new_game() {
833  // Seed the random number generator...
834  srand(time(NULL));
835
836  init();
837
838  interval_       = 0.1;
839  opened_columns_ = 0;
840
841  strcpy(title_, "Level: 1");
842  title_y_ = h();
843
844  redraw();
845}
846
847
848// Play/pause...
849void
850BlockWindow::play_cb(Fl_Widget *wi, BlockWindow *bw) {
851  if (bw->interval_ < 0) bw->new_game();
852  else bw->paused_ = !bw->paused_;
853
854  if (bw->paused_) wi->label("@>");
855  else {
856    wi->label("@-2||");
857    bw->help_ = false;
858  }
859}
860
861void BlockWindow::up_level() {
862  interval_ *= 0.95;
863  opened_columns_ = 0;
864  if (num_colors_ < 7) num_colors_ ++;
865  level_ ++;
866  sprintf(title_, "Level: %d", level_);
867  title_y_ = h();
868  Fl::repeat_timeout(interval_, (Fl_Timeout_Handler)timeout_cb, (void *)this);
869}
870
871// Animate the game...
872void
873BlockWindow::timeout_cb(BlockWindow *bw) {
874  int           i, j;
875  Block         *b;
876  Column        *c;
877  int           lastx, lasty;
878
879
880#ifdef DEBUG
881  struct timeval curtime;
882  static struct timeval lasttime;
883
884
885  gettimeofday(&curtime, NULL);
886  printf("%.3f (%+f - %f)\n",
887         curtime.tv_sec + 0.000001 * curtime.tv_usec,
888         curtime.tv_sec - lasttime.tv_sec +
889             0.000001 * (curtime.tv_usec - lasttime.tv_usec), bw->interval_);
890  lasttime = curtime;
891#endif // DEBUG
892
893  // Update blocks that have been destroyed...
894  for (i = 0, c = bw->columns_; i < bw->num_columns_; i ++, c ++)
895    for (j = 0, b = c->blocks; j < c->num_blocks; j ++, b ++)
896      if (b->color > (BLOCK_BLAST + 1)) {
897        bw->redraw();
898
899        c->num_blocks --;
900
901        if (j < c->num_blocks) {
902          memmove(b, b + 1, (c->num_blocks - j) * sizeof(Block));
903        }
904
905        j --;
906        b --;
907
908        if (!c->num_blocks) {
909          bw->num_columns_ --;
910
911          if (i < bw->num_columns_) {
912            memmove(c, c + 1, (bw->num_columns_ - i) * sizeof(Column));
913          }
914
915          i --;
916          c --;
917          j = c->num_blocks;
918        }
919      }
920
921  // Let the rest of the blocks fall and/or move...
922  for (i = bw->num_columns_, c = bw->columns_, lastx = c->x;
923       i > 0;
924       i --, c ++) {
925    if (c->x > lastx) {
926      c->x -= 8;
927      bw->redraw();
928    }
929
930    lastx = c->x + BLOCK_SIZE;
931
932    if (!bw->paused_ && bw->interval_ > 0.0) {
933      bw->redraw();
934      c->x ++;
935    }
936
937    for (j = c->num_blocks, b = c->blocks, lasty = 0; j > 0; j --, b ++) {
938      if (b->y > lasty) {
939        bw->redraw();
940        b->y -= 8;
941      }
942
943      lasty = b->y + BLOCK_SIZE;
944    }
945  }
946
947  // Slide the title text as needed...
948  if (bw->title_y_ > 0) {
949    bw->redraw();
950    bw->title_y_ -= 5;
951  }
952
953  // Play the game...
954  if (!bw->paused_ && bw->interval_ > 0.0) {
955    bw->count_ --;
956
957    if (bw->count_ <= 0) {
958      bw->redraw();
959      bw->count_ = BLOCK_SIZE;
960
961      if (bw->num_columns_ == BLOCK_COLS) {
962        bw->interval_ = -1.0;
963        bw->sound_->play_explosion(0.8);
964        bw->play_button_->label("@>");
965      } else {
966        bw->opened_columns_ ++;
967
968        if (bw->opened_columns_ > (2 * BLOCK_COLS)) {
969          bw->up_level();
970        }
971
972        c = bw->columns_;
973
974        if (bw->num_columns_) {
975          memmove(c + 1, c, bw->num_columns_ * sizeof(Column));
976        }
977
978        bw->num_columns_ ++;
979        c->x          = 0;
980        c->num_blocks = BLOCK_ROWS;
981
982        for (j = 0, b = c->blocks; j < BLOCK_ROWS; j ++, b ++) {
983          b->bomb  = bw->num_colors_ > 3 && (rand() & 127) < bw->num_colors_;
984          b->color = 1 + (rand() % bw->num_colors_);
985          b->y     = j * (BLOCK_SIZE + 8) + 24;
986        }
987      }
988    }
989  }
990  else
991  {
992    bw->count_ --;
993
994    if (bw->count_ <= 0) {
995      bw->count_ = 40;
996      bw->redraw();
997    }
998  }
999
1000  // Update the play/pause button as needed...
1001  if ((bw->paused_ || bw->interval_< 0.0) &&
1002      bw->play_button_->w() < 80) {
1003    int s = bw->play_button_->w() + 10;
1004
1005    bw->play_button_->resize(s, (s - 20) * (bw->h() - s) / 120, s, s);
1006    bw->play_button_->labelsize(s / 2 + 4);
1007    bw->redraw();
1008  } else if ((!bw->paused_ && bw->interval_ > 0.0) &&
1009             bw->play_button_->w() > 20) {
1010    int s = bw->play_button_->w() - 5;
1011
1012    bw->play_button_->resize(s, (s - 20) * (bw->h() - s) / 120, s, s);
1013    bw->play_button_->labelsize(s / 2 + 4);
1014    bw->redraw();
1015  }
1016
1017  if (bw->interval_ > 0.0) {
1018    Fl::repeat_timeout(bw->interval_, (Fl_Timeout_Handler)timeout_cb,
1019                       (void *)bw);
1020  } else {
1021    Fl::repeat_timeout(0.1, (Fl_Timeout_Handler)timeout_cb,
1022                       (void *)bw);
1023  }
1024}
1025
1026
1027//
1028// End of "$Id: blocks.cxx 7904 2010-11-28 21:12:59Z matt $".
1029//
Note: See TracBrowser for help on using the repository browser.