                         Fastgraph Tetris for Windows
                                by Diana Gruber

How to play tetris
------------------

Launch the game from Delphi, or from the Windows program manager. When the
play field appears, try to line up blocks in such a way as to fill a
horizontal row. When an entire row is filled, it will be munched by
langoliers. Then all the rows above it will move down a row. Keep doing this
until you get to the top of the playfield. At this point, you lose. Play the
game again. Keep doing it. This is entertaining. Honest, it is.

About the source code
---------------------

A form is created, and given a size calculated to be twice the size of the
virtual buffer. More about that in a minute. The form is called TForm1 and is
defined as follows:

  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Timer1: TTimer;
    Header1: THeader;
    procedure AppOnActivate(Sender: Tobject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure drop_block(Sender: TObject);
    procedure pause(Sender: TObject);
  end;

Four components are added to the form. These are:

   Panel1: a panel component, completely hidden by the button component.
   This component keeps the other components from constantly changing
   focus. Every time you press an arrow key, the focus would switch from
   the header to the button and back again, causing flickering. The
   panel fixes that.

   Button1: a button component, to allow the user to pause and resume the
   game.

   Timer1: the timer component, which triggers each frame of animation.

   Header1: the header component, which shows the score.

In addition, several functions are added to the form. Some of these are added
automatically through the object inspector. Others must be created manually.
The automatic ones have the letters "Form" as a prefix. Here are descriptions
of what these procedures do:

   procedure AppOnActivate(Sender: Tobject);

This is a manually created procedure that executes when control switches from
another Windows application to tetris, or in other words, when tetris becomes
the top-level window. It is analogous to the WM_SETFOCUS Windows message
handler. We use it in tetris to realize the application's logical palette, in
case the other application used a different palette.

   procedure FormCreate(Sender: TObject);

This procedure is called one time when the program begins. It is a good place
to set up the device context, logical palette, and virtual buffers. We also
enable the AppOnActivate() procedure. It is analogous to the WM_CREATE Windows
message handler.

   procedure FormDestroy(Sender: TObject);

This procedure is called one time when the program exits. It releases the
virtual buffers and other resources. It is analogous to the WM_DESTROY Windows
message handler.

   procedure FormPaint(Sender: TObject);

This procedure is called whenever any portion of the form needs to be redrawn.
In Delphi, the Invalidate() procedure called in AppOnActivate() will trigger
this. Other things can trigger this procedure too, such as when another Window
overlaps part of the form, or when the form size changes. It is analogous to
the WM_PAINT Windows message handler.

   procedure FormResize(Sender: TObject);

This procedure is called when the size of the form changes, and also as part
of the form creation. It simply records the form dimensions. It is analogous
to the WM_SIZE Windows message handler.

   procedure drop_block(Sender: TObject);

This function was manually created to handle the tetris animation. It is
triggered at regular intervals by the timer component. Each time this
procedure is called, it is considered a "frame of animation". Usually it
involves a block moving downward by several pictures, and the virtual buffer
is blitted to the form.

   procedure pause(Sender: TObject);

This function was manually created to allow the user to pause the game, for
example when the telephone rings, or when you want to run another application
while running tetris. It is triggered by the button component.

Several other procedures were added as well. These are called by the
drop_block() procedure, and handle some moving and displaying the blocks, and
playing the game.

Size of the frame, virtual buffers, and screen
----------------------------------------------

The virtual buffer must be scaled to fit in the window. The frame defines the
size of the window. If the size difference is a factor of two, the scaling
function does less work and is therefore faster. Of course, the user can scale
the window, and the program will still work. But for efficiency, the program
starts with a nice even multiple of the window size and the virtual buffer
size, as follows:

  Virtual buffer size:             240x200
  Frame size:                      480x400
  Play area:                        80x160

The entire virtual buffer is blitted to the frame during a Windows paint
event. During a timer event, however, when the game is animating, it is not
necessary to blit the entire buffer. Only the play area is blitted. This is
more efficient. I blitted the entire height of the virtual buffer (200 rows)
rather than calculating the length and scaling. Scaling factors are
unpredictable as windows are resized. A partial blit could cause an
undesirable stretching or jaggy effect.

Palette
-------

I grabbed the palette values from a PCX file and put them in an array. Then I
called fg_mapdacs() to translate the RGB components from the 0-63 range used
in DOS to the 0-255 range used in Windows. The logical palette is created
using fg_logpal(), passing it the 236 colors in the palette. This is set up in
the FormCreate() procedure. The palette is realized with a call to
fg_realize() in FormCreate() and also in AppOnActivate().

Important: remember you can only use 236 colors out of 256 in Windows. The
first 10 and last 10 are reserved for Windows. When designing bitmaps, etc.,
try to use the colors not reserved by Windows. That way, you will have more
control over them.

Animation
---------

That little langolier critter is cute, isn't he? He's a simple 256 color
bitmap in two frames. He is blitted to the virtual buffer using fg_drwimage().
The remnants of blocks he leaves behind as he munches are drawn with
single-color bitmaps using fg_drawmap(). The color is determined by looking at
the block before it is erased, with fg_getpixel(). Then the color is set with
fg_setcolor(). This is simple, and it works well.

Double-buffering is achieved by creating another virtual buffer, just slightly
larger than a row of blocks. This holds the clean copy of the blocks, so as
the langoliers munch they can be moved without leaving remnants.

Other Credits
-------------

Big thanks to Tim Feldman for explaining how to play WAV files in his article
in the Aug/Sep 1995 issue of PC Techniques. The sndPlaySound() function is a
great example of a useful but completely undocumented feature. Way to go,
Borland! (Not!)

Thanks, Tim! Also thanks to Tom Repstad and Steve Blackwood for programming
help.

For more information about Fastgraph for Windows, contact:
----------------------------------------------------------

Ted Gruber Software
P.O. Box 13408
Las Vegas, NV  89112

(702) 735-1980 (voice)
(702) 735-4603 (fax)
(702) 796-7134 (bbs)

Internet:
ftp.accessnv.com \fg for files
fastgraph@aol.com or 72000,1642@compuserve.com for email
