xenocara/app/xlockmore/modes/tetris.c
2006-11-26 11:07:42 +00:00

2551 lines
77 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* tetris --- autoplaying tetris game */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)tetris.c 5.01 2000/12/19 xlockmore";
#endif
/*-
* Copyright (c) 1998 by Jouk Jansen <joukj@hrem.stm.tudelft.nl>
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* This file is provided AS IS with no warranties of any kind. The author
* shall have no liability with respect to the infringement of copyrights,
* trade secrets or any patents by this file or any part thereof. In no
* event will the author be liable for any lost revenue or profits or
* other special, indirect and consequential damages.
*
* The author should like to be notified if changes have been made to the
* routine. Response will only be guaranteed when a VMS version of the
* program is available.
*
* An autoplaying tetris mode for xlockmore
* David Bagley changed much of it to look more like altetris code...
* which a significant portion belongs to Q. Alex Zhao.
*
* Copyright (c) 1992 - 95 Q. Alex Zhao, azhao@cc.gatech.edu
*
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the author not be
* used in advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* This program is distributed in the hope that it will be "playable",
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Todo list:
* -Add option for the level of thinking the computer should perform
* (i.e. dummy = do nothing ..... genius = do always the best move
* possible)
* -Get welltris option to work better.
* draw more sophisticated squares (i.e. something like the bitmaps
* on tetris)
*
* Revision History:
* 01-Dec-2000: Lifted code from polyominoes for variable size pieces
* Copyright (c) 2000 by Stephen Montgomery-Smith
* 01-Nov-2000: Allocation checks
* 02-Aug-2000: alwelltris trackmouse implemented
* 27-Jul-2000: alwelltris all but trackmouse
* 22-Jun-2000: trackmouse added, row clearing fixed, bonus pieces,
* 01-Nov-1998: altetris additions by David Bagley
* 01-Oct-1998: Created
*/
#ifdef STANDALONE
#define MODE_tetris
#define PROGCLASS "Tetris"
#define HACK_INIT init_tetris
#define HACK_DRAW draw_tetris
#define tetris_opts xlockmore_opts
#define DEFAULTS "*delay: 600000 \n" \
"*count: -500 \n" \
"*cycles: 200 \n" \
"*size: 0 \n" \
"*ncolors: 200 \n" \
"*fullrandom: True \n" \
"*trackmouse: False \n" \
"*verbose: False \n"
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#include "color.h"
#endif /* STANDALONE */
#ifdef MODE_tetris
#define DEF_BONUS "False"
#define DEF_CYCLE "True"
#define DEF_PLAIN "False"
#define DEF_TRACKMOUSE "False"
#define DEF_WELL "False"
static Bool bonus;
static Bool cycle_p;
static Bool plain;
static Bool trackmouse;
static Bool well;
#define INTRAND(min,max) (NRAND((max+1)-(min))+(min))
static XrmOptionDescRec opts[] =
{
{(char *) "-bonus", (char *) ".tetris.bonus", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+bonus", (char *) ".tetris.bonus", XrmoptionNoArg, (caddr_t) "off"},
{(char *) "-cycle", (char *) ".tetris.cycle", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+cycle", (char *) ".tetris.cycle", XrmoptionNoArg, (caddr_t) "off"},
{(char *) "-plain", (char *) ".tetris.plain", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+plain", (char *) ".tetris.plain", XrmoptionNoArg, (caddr_t) "off"},
#ifndef DISABLE_INTERACTIVE
{(char *) "-trackmouse", (char *) ".tetris.trackmouse", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+trackmouse", (char *) ".tetris.trackmouse", XrmoptionNoArg, (caddr_t) "off"},
#endif
{(char *) "-well", (char *) ".tetris.well", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+well", (char *) ".tetris.well", XrmoptionNoArg, (caddr_t) "off"}
};
static argtype vars[] =
{
{(void *) & bonus, (char *) "bonus", (char *) "Bonus", (char *) DEF_BONUS, t_Bool},
{(void *) & cycle_p, (char *) "cycle", (char *) "Cycle", (char *) DEF_CYCLE, t_Bool},
{(void *) & plain, (char *) "plain", (char *) "Plain", (char *) DEF_PLAIN, t_Bool},
#ifndef DISABLE_INTERACTIVE
{(void *) & trackmouse, (char *) "trackmouse", (char *) "TrackMouse", (char *) DEF_TRACKMOUSE, t_Bool},
#endif
{(void *) & well, (char *) "well", (char *) "Well", (char *) DEF_WELL, t_Bool}
};
static OptionStruct desc[] =
{
{(char *) "-/+bonus", (char *) "turn on/off larger bonus pieces"},
{(char *) "-/+cycle", (char *) "turn on/off colour cycling"},
{(char *) "-/+plain", (char *) "turn on/off plain pieces"},
#ifndef DISABLE_INTERACTIVE
{(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the mouse"},
#endif
{(char *) "-/+well", (char *) "turn on/off the welltris mode"}
};
ModeSpecOpt tetris_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#ifdef USE_MODULES
ModStruct tetris_description =
{"tetris", "init_tetris", "draw_tetris", "release_tetris",
"refresh_tetris", "change_tetris", (char *) NULL, &tetris_opts,
600000, -40, 200, -100, 64, 1.0, "",
"Shows an autoplaying tetris game", 0, NULL};
#endif
#include "bitmaps/gray1.xbm"
#define MINGRIDSIZE 10
#define MAXPIXELSIZE 12
#define MINSIZE 6
#define MAX_SIDES 4
#define NUM_FLASHES 16
#define BITMAPS 256
#define DELTA 1
#define WELL_WIDTH 8
#define WELL_DEPTH 12
#define WELL_PERIMETER (MAX_SIDES*WELL_WIDTH)
#define BASE_WIDTH (WELL_WIDTH*tp->xs+DELTA)
#define ENDPT (3*BASE_WIDTH)
#define THRESHOLD(x) ((x+1)*10)
#define EMPTY (-1)
typedef enum {FALL, DROP, MOVE_LEFT, MOVE_RIGHT, ROTATE, REFLECT} move_t;
typedef struct {
int squares, polyomino_number;
int xpos, ypos;
int size, color_number;
int random_rotation;
int random_reflection; /* not used */
long random_number;
} thing_t;
typedef struct {
int rotation;
int reflection; /* not used */
int leadingEmptyWidth, leadingEmptyHeight;
int *shape;
int size;
} Polyominoes;
typedef struct {
int number;
int *start;
} Mode;
typedef struct {
Polyominoes *polyomino;
Mode mode;
} Polytris;
static Polytris polytris[2];
typedef struct {
int pmid, cid;
} fieldstruct;
typedef struct {
Bool painted;
int object, orient;
GC gc;
Colormap cmap;
Bool cycle_p, mono_p, no_colors, use3D;
unsigned long blackpixel, whitepixel, fg, bg;
int direction;
unsigned long colour;
int p_type;
int sidemoves;
int xb, yb;
int xs, ys;
int width, height;
int pixelmode;
XImage *images[BITMAPS];
XColor *colors;
int ncolors;
int ncols, nrows;
int rows, level;
Bool bonus, bonusNow;
thing_t curPolyomino;
/* tetris */
fieldstruct *field;
/* welltris */
Bool well;
int frozen_wall[MAX_SIDES];
fieldstruct wall[WELL_DEPTH+WELL_WIDTH][WELL_PERIMETER];
fieldstruct base[WELL_WIDTH][WELL_WIDTH];
Pixmap graypix;
ModeInfo *mi;
} trisstruct;
#define ARR(i,j) (((i)<0||(i)>=tp->curPolyomino.size||\
(j)<0||(j)>=tp->curPolyomino.size)?-2:\
(polytris[(int) tp->bonusNow].polyomino[tp->curPolyomino.polyomino_number].shape[(j) * tp->curPolyomino.size + (i)]>0))
#define ROUND8(n) ((((n)+7)/8)*8)
/* Defines to index the bitmaps. A set bit indicates that an edge or
corner is required. */
#define LEFT 1 /* (1<<0) */
#define DOWN 2 /* (1<<1) */
#define RIGHT 4 /* (1<<2) */
#define UP 8 /* (1<<3) */
#define LEFT_DOWN 16 /* (1<<4) */
#define RIGHT_DOWN 32 /* (1<<5) */
#define RIGHT_UP 64 /* (1<<6) */
#define LEFT_UP 128 /* (1<<7) */
#define IS_LEFT(n) ((n)&LEFT)
#define IS_DOWN(n) ((n)&DOWN)
#define IS_RIGHT(n) ((n)&RIGHT)
#define IS_UP(n) ((n)&UP)
#define IS_LEFT_DOWN(n) ((n)&LEFT_DOWN)
#define IS_RIGHT_DOWN(n) ((n)&RIGHT_DOWN)
#define IS_RIGHT_UP(n) ((n)&RIGHT_UP)
#define IS_LEFT_UP(n) ((n)&LEFT_UP)
#define CHECKLEFT(x) ((x)|LEFT)
#define CHECKDOWN(x) ((x)|DOWN)
#define CHECKRIGHT(x) ((x)|RIGHT)
#define CHECKUP(x) ((x)|UP)
/* Defines to access the bitmaps. */
#define BITNO(x,y) ((x)+(y)*ROUND8(tp->ys))
#define SETBIT(x,y) {data[BITNO(x,y)/8] |= 1<<(BITNO(x,y)%8);}
#define RESBIT(x,y) {data[BITNO(x,y)/8] &= ~(1<<(BITNO(x,y)%8));}
#define TWOTHIRDSBIT(x,y) {if ((x+y-1)%3) SETBIT(x,y) else RESBIT(x,y)}
#define HALFBIT(x,y) {if ((x-y)%2) SETBIT(x,y) else RESBIT(x,y)}
#define THIRDBIT(x,y) {if (!((x-y-1)%3)) SETBIT(x,y) else RESBIT(x,y)}
#define THREEQUARTERSBIT(x,y) \
{if ((y%2)||((x+2+y/2+1)%2)) SETBIT(x,y) else RESBIT(x,y)}
#define NOTHALFBIT(x,y) {if ((x-y)%2) RESBIT(x,y) else SETBIT(x,y)}
/* Parameters for bitmaps. */
#define G ((tp->ys/45)+1) /* 1/2 of gap between pentominoes. */
#define T ((tp->ys<=12)?1:(G*2)) /* Thickness of walls of pentominoes. */
#define R ((tp->ys<=12)?1:(G*6)) /* Amount of rounding. */
#define RT ((tp->ys<=12)?1:(G*3)) /* Thickness of wall of rounded parts.
Here 3 is an approximation to 2 sqrt(2). */
#define RR 0 /* Roof ridge thickness */
/* A list of those bitmaps we need to create to display any polyomino. */
static int images_needed[] =
{
/* This needed for mononimo*/
LEFT|RIGHT|UP|DOWN|LEFT_UP|LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,
LEFT_UP|LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,
LEFT|RIGHT_UP|RIGHT_DOWN,
RIGHT|LEFT_UP|LEFT_DOWN,
UP|LEFT_DOWN|RIGHT_DOWN,
DOWN|LEFT_UP|RIGHT_UP,
LEFT|RIGHT_UP,
RIGHT|LEFT_UP,
UP|LEFT_DOWN,
DOWN|LEFT_UP,
LEFT|RIGHT_DOWN,
RIGHT|LEFT_DOWN,
UP|RIGHT_DOWN,
DOWN|RIGHT_UP,
#if 0
/* These needed for hexonimoes*/
LEFT,
RIGHT,
UP,
DOWN,
LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,
LEFT_UP|RIGHT_UP|RIGHT_DOWN,
LEFT_UP|LEFT_DOWN|RIGHT_DOWN,
LEFT_UP|LEFT_DOWN|RIGHT_UP,
#endif
LEFT|UP|RIGHT_DOWN,
LEFT|DOWN|RIGHT_UP,
RIGHT|UP|LEFT_DOWN,
RIGHT|DOWN|LEFT_UP,
LEFT|UP,
LEFT|DOWN,
RIGHT|UP,
RIGHT|DOWN,
LEFT|RIGHT,
UP|DOWN,
RIGHT|UP|DOWN,
LEFT|UP|DOWN,
LEFT|RIGHT|DOWN,
LEFT|RIGHT|UP,
-1
};
static trisstruct *triss = (trisstruct *) NULL;
/* Setup: Normal, Bonus */
static int area_kinds[] = {3, 3};
static int omino_squares[] = {4, 5};
static int start_ominoes[] = {7, 18};
static int max_ominoes[] = {19, 63};
static int
ominos[] =
{
/* Side of smallest square that contains shape */
/* Number of orientations */
/* Position, Next Postion, (not used yet),
number_down, used_for_start?
Polyomino (side^2 values) */
/* Normal pieces 4 squares */
2,
1,
0, 0, 0, 2, 6, 3, 12, 9,
3,
16,
0, 1, 4, 2, 0, 0, 0, 6, 5, 1, 8, 0, 0,
1, 2, 7, 0, 0, 2, 0, 0, 10, 0, 0, 12, 1,
2, 3, 6, 0, 0, 0, 0, 0, 0, 2, 4, 5, 9,
3, 0, 5, 0, 0, 4, 3, 0, 0, 10, 0, 0, 8,
4, 5, 0, 1, 0, 0, 0, 4, 5, 3, 0, 0, 8,
5, 6, 3, 0, 0, 6, 1, 0, 10, 0, 0, 8, 0,
6, 7, 2, 0, 0, 0, 0, 2, 0, 0, 12, 5, 1,
7, 4, 1, 0, 0, 0, 2, 0, 0, 10, 0, 4, 9,
8, 9, 8, 2, 0, 0, 0, 4, 7, 1, 0, 8, 0,
9, 10, 11, 0, 0, 2, 0, 0, 14, 1, 0, 8, 0,
10, 11, 10, 0, 0, 0, 0, 0, 2, 0, 4, 13, 1,
11, 8, 9, 0, 0, 0, 2, 0, 4, 11, 0, 0, 8,
12, 13, 14, 2, 0, 0, 0, 4, 3, 0, 0, 12, 1,
13, 12, 15, 0, 0, 0, 2, 0, 6, 9, 0, 8, 0,
14, 15, 12, 1, 0, 0, 0, 0, 6, 1, 4, 9, 0,
15, 14, 13, 0, 0, 2, 0, 0, 12, 3, 0, 0, 8,
4,
2,
0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 1, 0, 0, 0, 0,
1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 8, 0,
/* Bonus pieces 5 squares */
3,
37,
0, 1, 0, 2, 0, 0, 0, 6, 5, 3, 8, 0, 8,
1, 2, 3, 0, 0, 6, 1, 0, 10, 0, 0, 12, 1,
2, 3, 2, 0, 0, 0, 0, 2, 0, 2, 12, 5, 9,
3, 0, 1, 0, 0, 4, 3, 0, 0, 10, 0, 4, 9,
4, 5, 8, 2, 0, 0, 0, 6, 7, 1, 12, 9, 0,
5, 6, 11, 0, 0, 2, 0, 0, 14, 3, 0, 12, 9,
6, 7, 10, 0, 0, 0, 0, 0, 6, 3, 4, 13, 9,
7, 4, 9, 0, 0, 6, 3, 0, 12, 11, 0, 0, 8,
8, 9, 4, 1, 0, 0, 0, 4, 7, 3, 0, 12, 9,
9, 10, 7, 0, 0, 6, 3, 0, 14, 9, 0, 8, 0,
10, 11, 6, 0, 0, 0, 0, 6, 3, 0, 12, 13, 1,
11, 8, 5, 0, 0, 0, 2, 0, 6, 11, 0, 12, 9,
12, 13, 12, 2, 4, 7, 1, 0, 10, 0, 0, 8, 0,
13, 14, 15, 0, 2, 0, 0, 14, 5, 1, 8, 0, 0,
14, 15, 14, 0, 0, 2, 0, 0, 10, 0, 4, 13, 1,
15, 12, 13, 0, 0, 0, 2, 4, 5, 11, 0, 0, 8,
16, 17, 19, 2, 6, 5, 1, 10, 0, 0, 8, 0, 0,
17, 18, 18, 0, 2, 0, 0, 10, 0, 0, 12, 5, 1,
18, 19, 17, 0, 0, 0, 2, 0, 0, 10, 4, 5, 9,
19, 16, 16, 0, 4, 5, 3, 0, 0, 10, 0, 0, 8,
20, 21, 22, 2, 2, 0, 0, 12, 5, 3, 0, 0, 8,
21, 20, 23, 0, 0, 6, 1, 0, 10, 0, 4, 9, 0,
22, 23, 20, 1, 0, 0, 2, 6, 5, 9, 8, 0, 0,
23, 22, 21, 0, 4, 3, 0, 0, 10, 0, 0, 12, 1,
24, 25, 25, 2, 4, 3, 0, 0, 12, 3, 0, 0, 8,
25, 26, 24, 0, 0, 6, 1, 6, 9, 0, 8, 0, 0,
26, 27, 27, 0, 2, 0, 0, 12, 3, 0, 0, 12, 1,
27, 24, 26, 0, 0, 0, 2, 0, 6, 9, 4, 9, 0,
28, 29, 32, 2, 0, 2, 0, 6, 13, 1, 8, 0, 0,
29, 30, 35, 0, 0, 2, 0, 4, 11, 0, 0, 12, 1,
30, 31, 34, 0, 0, 0, 2, 4, 7, 9, 0, 8, 0,
31, 28, 33, 0, 4, 3, 0, 0, 14, 1, 0, 8, 0,
32, 33, 28, 1, 0, 2, 0, 4, 13, 3, 0, 0, 8,
33, 34, 31, 0, 0, 6, 1, 4, 11, 0, 0, 8, 0,
34, 35, 30, 0, 2, 0, 0, 12, 7, 1, 0, 8, 0,
35, 32, 29, 0, 0, 2, 0, 0, 14, 1, 4, 9, 0,
36, 36, 36, 2, 0, 2, 0, 4, 15, 1, 0, 8, 0,
4,
24,
0, 1, 4, 2, 0, 0, 0, 0, 6, 5, 5, 1, 8, 0, 0, 0, 0, 0, 0, 0,
1, 2, 7, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 12, 1, 0,
2, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 5, 5, 9, 0, 0, 0, 0,
3, 0, 5, 0, 0, 4, 3, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 8, 0,
4, 5, 0, 1, 0, 0, 0, 0, 4, 5, 5, 3, 0, 0, 0, 8, 0, 0, 0, 0,
5, 6, 3, 0, 0, 6, 1, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 8, 0, 0,
6, 7, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 12, 5, 5, 1, 0, 0, 0, 0,
7, 4, 1, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 4, 9, 0,
8, 9, 12, 2, 0, 0, 0, 0, 4, 7, 5, 1, 0, 8, 0, 0, 0, 0, 0, 0,
9, 10, 15, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 14, 1, 0, 0, 8, 0, 0,
10, 11, 14, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 5, 13, 1, 0, 0, 0, 0,
11, 8, 13, 0, 0, 0, 2, 0, 0, 4, 11, 0, 0, 0, 10, 0, 0, 0, 8, 0,
12, 13, 8, 1, 0, 0, 0, 0, 4, 5, 7, 1, 0, 0, 8, 0, 0, 0, 0, 0,
13, 14, 11, 0, 0, 2, 0, 0, 0, 14, 1, 0, 0, 10, 0, 0, 0, 8, 0, 0,
14, 15, 10, 0, 0, 0, 0, 0, 0, 2, 0, 0, 4, 13, 5, 1, 0, 0, 0, 0,
15, 12, 9, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 4, 11, 0, 0, 0, 8, 0,
16, 17, 20, 2, 0, 0, 0, 0, 4, 5, 3, 0, 0, 0, 12, 1, 0, 0, 0, 0,
17, 18, 23, 0, 0, 0, 2, 0, 0, 6, 9, 0, 0, 10, 0, 0, 0, 8, 0, 0,
18, 19, 22, 0, 0, 0, 0, 0, 4, 3, 0, 0, 0, 12, 5, 1, 0, 0, 0, 0,
19, 16, 21, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 6, 9, 0, 0, 8, 0, 0,
20, 21, 16, 1, 0, 0, 0, 0, 0, 6, 5, 1, 4, 9, 0, 0, 0, 0, 0, 0,
21, 22, 19, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 12, 3, 0, 0, 0, 8, 0,
22, 23, 18, 0, 0, 0, 0, 0, 0, 0, 6, 1, 4, 5, 9, 0, 0, 0, 0, 0,
23, 20, 17, 0, 0, 2, 0, 0, 0, 12, 3, 0, 0, 0, 10, 0, 0, 0, 8, 0,
5,
2,
0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 10, 0, 0, 0, 0, 10, 0, 0, 0, 0, 8, 0, 0,
};
static int
image_needed(int n) {
int i;
for (i=0;images_needed[i]!=-1;i++)
if (n == images_needed[i])
return 1;
return 0;
}
static void
leadingEmptyWidth(Polyominoes * polyo)
{
int i, j;
for (i = 0; i < polyo->size; i++) {
for (j = 0; j < polyo->size; j++) {
if (polyo->shape[j * polyo->size + i]) {
polyo->leadingEmptyWidth = i;
return;
}
}
}
polyo->leadingEmptyWidth = polyo->size;
}
static void
leadingEmptyHeight(Polyominoes * polyo)
{
int i, j;
for (j = 0; j < polyo->size; j++) {
for (i = 0; i < polyo->size; i++) {
if (polyo->shape[j * polyo->size + i]) {
polyo->leadingEmptyHeight = j;
return;
}
}
}
polyo->leadingEmptyHeight = polyo->size;
}
static Bool
readPolyominoes(void)
{
int size, n, polyindex = 0, sum = 0;
int counter, start_counter;
int polyomino, kinds, i, j, bonustype;
for (bonustype = 0; bonustype < 2; bonustype++) {
counter = 0, start_counter = 0;
polytris[bonustype].mode.number = start_ominoes[bonustype];
for (kinds = 0; kinds < area_kinds[bonustype]; kinds++) {
size = ominos[polyindex++];
if (size > omino_squares[bonustype]) {
(void) printf("tetris corruption: size %d\n", size);
return False;
}
n = ominos[polyindex++];
if (sum > max_ominoes[bonustype]) {
(void) printf("tetris corruption: n %d\n", n);
return False;
}
for (polyomino = 0; polyomino < n; polyomino++) {
if (polyomino + counter < max_ominoes[bonustype]) {
sum = polyomino + counter;
} else {
(void) printf("tetris corruption: bonustype %d, polyomino %d, counter %d\n",
bonustype, polyomino, counter);
return False;
}
if (polyomino != ominos[polyindex]) {
(void) printf("tetris corruption: polyomino %d, ominos[polyindex] %d\n",
polyomino, ominos[polyindex]);
return False;
}
polyindex++; /* This is only there to "read" it */
polytris[bonustype].polyomino[sum].rotation =
ominos[polyindex++] + counter;
polytris[bonustype].polyomino[sum].reflection =
ominos[polyindex++] + counter; /* not used */
if (ominos[polyindex++]) {
if (start_counter < start_ominoes[bonustype]) {
polytris[bonustype].mode.start[start_counter++] = sum;
} else {
(void) printf("tetris corruption: bonustype %d, polyomino %d, start_counter %d\n",
bonustype, polyomino, start_counter);
return False;
}
}
polytris[bonustype].polyomino[sum].size = size;
if ((polytris[bonustype].polyomino[sum].shape = (int *) malloc(size *
size * sizeof(int))) == NULL) {
(void) printf("tetris out of memory\n");
return False;
}
for (j = 0; j < size; j++) {
for (i = 0; i < size; i++) {
polytris[bonustype].polyomino[sum].shape[j * size + i] =
ominos[polyindex++];
}
}
leadingEmptyWidth(&(polytris[bonustype].polyomino[sum]));
leadingEmptyHeight(&(polytris[bonustype].polyomino[sum]));
}
counter += n;
}
}
return True;
}
#ifdef DEBUG
static void
writePolyominoes(trisstruct * tp)
{
int size = 0;
int polyomino, i, j, bonustype;
for (bonustype = 0; bonustype < 2; bonustype++) {
for (polyomino = 0; polyomino < max_ominoes[bonustype]; polyomino++) {
(void) printf("polyomino %d, rotation %d, reflection %d, size %d\n",
polyomino, polytris[bonustype].polyomino[polyomino].rotation,
polytris[bonustype].polyomino[polyomino].reflection,
polytris[bonustype].polyomino[polyomino].size);
size = polytris[bonustype].polyomino[polyomino].size;
for (j = 0; j < size; j++) {
(void) printf(" ");
for (i = 0; i < size; i++) {
(void) printf(" %d", polytris[bonustype].polyomino[polyomino].shape[j * size + i]);
}
}
(void) printf("\n");
}
(void) printf("Starts:");
for (polyomino = 0; polyomino < start_ominoes[bonustype]; polyomino++) {
(void) printf(" %d", polytris[bonustype].mode.start[polyomino]);
}
(void) printf("\n");
}
}
#endif
static void
setColour(ModeInfo * mi, int cid /* , int exor */)
{
Display *display = MI_DISPLAY(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
long colour;
if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
if (tp->ncolors >= start_ominoes[(int) tp->bonusNow] + 2)
colour = cid + 2;
else
colour = 1; /* Just in case */
XSetBackground(display, tp->gc, tp->blackpixel);
XSetForeground(display, tp->gc, tp->colors[colour].pixel);
} else {
if (MI_NPIXELS(mi) > start_ominoes[(int) tp->bonusNow])
colour = MI_PIXEL(mi, cid * MI_NPIXELS(mi) / start_ominoes[(int) tp->bonusNow]);
else
colour = MI_WHITE_PIXEL(mi);
XSetForeground(display, tp->gc, colour);
XSetBackground(display, tp->gc, MI_BLACK_PIXEL(mi));
}
}
static void
drawSquare(ModeInfo * mi, int pmid, int cid, int col, int row)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
setColour(mi, cid /* , 0 */);
if (tp->pixelmode)
XFillRectangle(display, window, tp->gc,
tp->xb + col * tp->xs, tp->yb + row * tp->ys, tp->xs, tp->ys);
else {
if (pmid < 0 || pmid >= 256)
(void) printf("pmid = %d\n", pmid);
/*-
* PURIFY on SunOS4 and on Solaris 2 reports a 120 byte memory leak on
* the next line */
(void) XPutImage(display, window, tp->gc, tp->images[pmid], 0, 0,
tp->xb + col * tp->xs, tp->yb + row * tp->ys,
tp->xs, tp->ys);
}
}
static void
xorSquare(ModeInfo * mi, int i, int j)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
XSetFunction(display, tp->gc, GXxor);
XSetBackground(display, tp->gc, 0);
XSetForeground(display, tp->gc, 0xFFFFFF);
XFillRectangle(display, window, tp->gc,
tp->xb + i * tp->xs, tp->yb + j * tp->ys,
tp->xs, tp->ys);
XSetFunction(display, tp->gc, GXcopy);
XSetBackground(display, tp->gc, tp->blackpixel);
XSetForeground(display, tp->gc, tp->whitepixel);
}
static void
XFillTrapazoid(Display *display, Window window, GC gc,
int ulx, int uly, int base_x, int base_y, int lrx, int lry,
int plateau_x, int plateau_y)
{
XPoint trapazoid_list[MAX_SIDES];
trapazoid_list[0].x = ulx;
trapazoid_list[0].y = uly;
trapazoid_list[1].x = base_x;
trapazoid_list[1].y = base_y;
trapazoid_list[2].x = lrx - base_x - ulx;
trapazoid_list[2].y = lry - base_y - uly;
trapazoid_list[3].x = -plateau_x;
trapazoid_list[3].y = -plateau_y;
XFillPolygon(display, window, gc, trapazoid_list, MAX_SIDES,
Convex, CoordModePrevious);
}
static void
drawWallSquare(ModeInfo * mi, GC gc, int i, int j)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
int ulx, uly, lrx, lry, base_x, plateau_x, w, offsetx, offsety;
offsetx = tp->width / 2 - 3 * (WELL_WIDTH * tp->xs) / 2 - DELTA;
offsety = tp->height / 2 - 3 * (WELL_WIDTH * tp->xs) / 2 - DELTA;
w = i / WELL_WIDTH;
i -= (w * WELL_WIDTH);
ulx = 3 * BASE_WIDTH / 2 + (i - (WELL_WIDTH / 2)) *
(2 * tp->xs * (WELL_DEPTH - j) / WELL_DEPTH + tp->xs) +
2 * DELTA;
uly = j * WELL_WIDTH * tp->xs / WELL_DEPTH + 2 * DELTA;
lrx = 3 * BASE_WIDTH / 2 + (i + 1 - (WELL_WIDTH / 2)) *
(2 * tp->xs * (WELL_DEPTH - j - 1) / WELL_DEPTH + tp->xs) +
DELTA * (i / 4) - 3 * DELTA;
lry = (j + 1) * WELL_WIDTH * tp->xs / WELL_DEPTH - DELTA;
base_x = tp->xs + (WELL_DEPTH - j) * 2 * tp->xs / WELL_DEPTH -
5 * DELTA;
plateau_x = tp->xs + (WELL_DEPTH - j - 1) * 2 *
tp->xs / WELL_DEPTH - 5 * DELTA;
{
XSetFillStyle(display, gc, FillOpaqueStippled);
XSetStipple(display, gc, tp->graypix);
}
switch (w) {
case 0:
XFillTrapazoid(display, window, gc,
ulx + offsetx, uly + offsety, base_x, 0,
lrx + offsetx, lry + offsety, plateau_x, 0);
break;
case 1:
XFillTrapazoid(display, window, gc,
ENDPT - uly + offsetx, ulx + offsety, 0, base_x,
ENDPT - lry + offsetx, lrx + offsety, 0, plateau_x);
break;
case 2:
XFillTrapazoid(display, window, gc,
ENDPT - 1 - ulx + offsetx, ENDPT - uly + offsety, -base_x, 0,
ENDPT - 1 - lrx + offsetx, ENDPT - lry + offsety, -plateau_x, 0);
break;
case 3:
XFillTrapazoid(display, window, gc,
uly + offsetx, ENDPT - 1 - ulx + offsety, 0, -base_x,
lry + offsetx, ENDPT - 1 - lrx + offsety, 0, -plateau_x);
break;
default:
(void) fprintf (stderr, "trapazoid kinds range 0-3, got %d.\n", w);
}
{
XSetFillStyle(display, gc, FillSolid);
}
}
static void
clearTrapazoid(ModeInfo * mi, int i, int j)
{
Display *display = MI_DISPLAY(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
XSetForeground(display, tp->gc, tp->blackpixel);
drawWallSquare(mi, tp->gc, i, j); /* pmid not used yet */
}
static void
drawTrapazoid(ModeInfo * mi, /* int pmid, */ int cid, int i, int j)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
setColour(mi, cid /* , 0 */ );
drawWallSquare(mi, tp->gc, i, j); /* pmid not used yet */
}
static void
freezeTrapazoid(ModeInfo * mi, int i, int j)
{
Display *display = MI_DISPLAY(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
XSetFunction(display, tp->gc, GXxor);
XSetBackground(display, tp->gc, 0);
XSetForeground(display, tp->gc, 0xFFFFFF);
drawWallSquare(mi, tp->gc, i, j); /* pmid not used yet */
XSetFunction(display, tp->gc, GXcopy);
XSetBackground(display, tp->gc, tp->blackpixel);
XSetForeground(display, tp->gc, tp->whitepixel);
}
static int
checkWall(int pmid, int base_i, int base_j)
{
if (pmid < 0)
return pmid;
/* Check if next to well wall */
if (base_i == 0)
pmid = CHECKLEFT((unsigned int) pmid);
if (base_j == 0)
pmid = CHECKUP((unsigned int) pmid);
if (base_i == WELL_WIDTH - 1)
pmid = CHECKRIGHT((unsigned int) pmid);
if (base_j == WELL_WIDTH - 1)
pmid = CHECKDOWN((unsigned int) pmid);
return pmid;
}
static int
checkSides(int pmid, int i)
{
if (pmid < 0)
return pmid;
if (i == 0 || i == 3 * WELL_WIDTH - 1)
pmid = CHECKLEFT((unsigned int) pmid);
else if (i == WELL_WIDTH - 1 || i == 2 * WELL_WIDTH)
pmid = CHECKRIGHT((unsigned int) pmid);
else if (i == WELL_WIDTH || i == 4 * WELL_WIDTH - 1)
pmid = CHECKUP((unsigned int) pmid);
else if (i == 2 * WELL_WIDTH - 1 || i == 3 * WELL_WIDTH)
pmid = CHECKDOWN((unsigned int) pmid);
return pmid;
}
static int
checkBottom(int pmid, int i, int j)
{
if (pmid < 0 || j != WELL_DEPTH - 1)
return pmid;
switch (i / WELL_WIDTH) {
case 0:
return CHECKDOWN((unsigned int) pmid);
case 1:
return CHECKLEFT((unsigned int) pmid);
case 2:
return CHECKUP((unsigned int) pmid);
case 3:
return CHECKRIGHT((unsigned int) pmid);
}
return EMPTY;
}
/* Wall number ... imagine a 4 hour clock...
0
3+1
2
*/
static void
wall_to_base(int *base_x, int *base_y, int wall_x, int wall_y)
{
switch (wall_x / WELL_WIDTH)
{
case 0:
*base_x = wall_x;
*base_y = wall_y - WELL_DEPTH;
break;
case 1:
*base_x = WELL_WIDTH - 1 + WELL_DEPTH - wall_y;
*base_y = wall_x - WELL_WIDTH;
break;
case 2:
*base_x = WELL_PERIMETER - 1 - WELL_WIDTH - wall_x;
*base_y = WELL_WIDTH - 1 + WELL_DEPTH - wall_y;
break;
case 3:
*base_x = wall_y - WELL_DEPTH;
*base_y = WELL_PERIMETER - 1 - wall_x;
break;
default:
(void) fprintf (stderr, "wall_to_base kinds range 0-3, got %d.\n",
wall_x / WELL_WIDTH);
}
}
static void
dropWall(ModeInfo *mi, int w)
{
Display *display = MI_DISPLAY(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j, k, l, lines, base_x, base_y;
Bool fits;
lines = 0;
for (j = WELL_DEPTH - 1; j >= 0 && lines == 0; j--)
for (i = 0; i < WELL_WIDTH; i++)
if (tp->wall[j][w * WELL_WIDTH + i].pmid != EMPTY)
lines = WELL_DEPTH - j;
if (lines > 0) {
fits = True;
j = 0;
while (j < (WELL_WIDTH / 2 + lines - 1) && fits) {
j++;
for (l = WELL_DEPTH - j; l < WELL_DEPTH; l++)
for (i = 0; i < WELL_WIDTH; i++)
if (tp->wall[l][w * WELL_WIDTH + i].pmid != EMPTY) {
wall_to_base(&base_x, &base_y,
w * WELL_WIDTH + i, l + j);
if (tp->base[base_y][base_x].pmid != EMPTY)
fits = False;
}
}
if (!fits)
j--;
if (j > 0)
{
for (l = WELL_DEPTH - 1; l >= 0; l--)
for (i = 0; i < WELL_WIDTH; i++)
if (tp->wall[l][w * WELL_WIDTH + i].pmid != EMPTY) {
k = w * WELL_WIDTH + i;
if (l + j >= WELL_DEPTH) {
wall_to_base(&base_x, &base_y, k, l + j);
tp->base[base_y][base_x] = tp->wall[l][k];
tp->base[base_y][base_x].pmid =
checkWall(tp->base[base_y][base_x].pmid, base_x, base_y);
tp->wall[l][k].pmid = EMPTY;
clearTrapazoid(mi, k, l);
drawSquare(mi, tp->base[base_y][base_x].pmid,
tp->base[base_y][base_x].cid, base_x, base_y);
} else {
tp->wall[l + j][k] = tp->wall[l][k];
tp->wall[l + j][k].pmid =
checkBottom(tp->wall[l + j][k].pmid, k, l + j);
tp->wall[l][k].pmid = EMPTY;
clearTrapazoid(mi, k, l);
drawTrapazoid(mi, /* tp->wall[l][k].pmid, */ tp->wall[l][k].cid,
k, l + j);
}
XFlush(display);
}
}
}
}
static void
freezeWall(ModeInfo * mi, int w)
{
int i, j;
for (j = 0; j < WELL_DEPTH; j++)
for (i = 0; i < WELL_WIDTH; i++) {
freezeTrapazoid(mi, w * WELL_WIDTH + i, j);
}
XFlush(MI_DISPLAY(mi));
}
static Bool
allFrozen(ModeInfo * mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int w;
for (w = 0; w < MAX_SIDES; w++)
if (tp->frozen_wall[w] == MAX_SIDES) {
freezeWall(mi, w);
XFlush(MI_DISPLAY(mi));
}
for (w = 0; w < MAX_SIDES; w++)
if (!tp->frozen_wall[w])
return False;
return True;
}
static void
checkFreeze(ModeInfo *mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int w;
for (w = 0; w < MAX_SIDES; w++) {
if (!tp->frozen_wall[w]) {
dropWall(mi, w);
} else {
tp->frozen_wall[w]--;
if (!tp->frozen_wall[w]) {
freezeWall(mi, w);
dropWall(mi, w);
}
}
}
}
static int
curPixmap(trisstruct *tp, int i, int j)
{
int pmid = 0;
if (ARR(i,j) != ARR(i-1,j))
pmid |= LEFT;
if (ARR(i,j) != ARR(i,j+1))
pmid |= DOWN;
if (ARR(i,j) != ARR(i+1,j))
pmid |= RIGHT;
if (ARR(i,j) != ARR(i,j-1))
pmid |= UP;
if (ARR(i,j) != ARR(i-1,j+1))
pmid |= LEFT_DOWN;
if (ARR(i,j) != ARR(i+1,j+1))
pmid |= RIGHT_DOWN;
if (ARR(i,j) != ARR(i+1,j-1))
pmid |= RIGHT_UP;
if (ARR(i,j) != ARR(i-1,j-1))
pmid |= LEFT_UP;
return pmid;
}
static int
rotateIndex(int pmid, int i)
{
int wallRotation, pm1, pm0;
if (pmid == EMPTY)
return EMPTY;
wallRotation = (MAX_SIDES - i / WELL_WIDTH) % MAX_SIDES;
pm1 = (0xF0 & pmid) << wallRotation;
pm0 = (0xF & pmid) << wallRotation;
pm1 = (0xF0 & pm1) | ((pm1 & 0xF00) >> MAX_SIDES);
pm0 = (0xF & pm0) | ((pm0 & 0xF0) >> MAX_SIDES);
return (pm1 | pm0);
}
static void
drawBox(ModeInfo * mi, int pmid, int cid, int i, int j)
{
if (j < WELL_DEPTH) {
drawTrapazoid(mi, /* pmid, */ cid, i, j);
} else {
int base_i, base_j;
wall_to_base(&base_i, &base_j, i, j);
drawSquare(mi, pmid, cid, base_i, base_j);
}
}
static void
clearSquare(ModeInfo * mi, int col, int row)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
XSetForeground(display, tp->gc, tp->blackpixel);
XFillRectangle(display, window, tp->gc,
tp->xb + col * tp->xs, tp->yb + row * tp->ys, tp->xs, tp->ys);
}
static void
drawPolyomino(ModeInfo * mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j;
if (tp->well) {
int xi, yj, base_i, base_j;
int temp_base[WELL_WIDTH][WELL_WIDTH];
/* Initialize */
for (i = 0; i < WELL_WIDTH; i++)
for (j = 0; j < WELL_WIDTH; j++)
temp_base[j][i] = 0;
/* Find pieces and overlap them if necessary */
for (i = 0; i < tp->curPolyomino.size; i++) {
for (j = 0; j < tp->curPolyomino.size; j++) {
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape
[j * tp->curPolyomino.size + i]) {
xi = (tp->curPolyomino.xpos + i) % WELL_PERIMETER;
yj = tp->curPolyomino.ypos + j;
if (yj >= WELL_DEPTH) {
wall_to_base(&base_i, &base_j, xi, yj);
xi = checkWall(rotateIndex(curPixmap(tp, i, j), xi),
base_i, base_j);
if (!temp_base[base_j][base_i])
temp_base[base_j][base_i] = xi;
else
temp_base[base_j][base_i] &= xi;
}
}
}
/* Now draw */
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape
[j * tp->curPolyomino.size + i]) {
xi = (tp->curPolyomino.xpos + i) % WELL_PERIMETER;
yj = tp->curPolyomino.ypos + j;
if (yj >= WELL_DEPTH) {
wall_to_base(&base_i, &base_j, xi, yj);
if (temp_base[base_j][base_i]) {
drawSquare(mi, temp_base[base_j][base_i],
tp->curPolyomino.color_number, base_i, base_j);
temp_base[base_j][base_i] = 0;
}
} else if (yj >= 0) {
drawTrapazoid(mi, /* tp->curPolyomino.pmid, */
tp->curPolyomino.color_number, xi, yj);
}
}
}
} else {
for (i = 0; i < tp->curPolyomino.size; i++) {
for (j = 0; j < tp->curPolyomino.size; j++) {
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape
[j * tp->curPolyomino.size + i]) {
drawSquare(mi, curPixmap(tp, i, j),
tp->curPolyomino.color_number,
tp->curPolyomino.xpos + i, tp->curPolyomino.ypos + j);
}
}
}
}
}
static void
drawPolyominoDiff(ModeInfo *mi, thing_t * old)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j, ox, oy;
if (tp->well) {
int xi, yj, base_i, base_j;
int temp_base[WELL_WIDTH][WELL_WIDTH];
/* Initialize */
for (i = 0; i < WELL_WIDTH; i++)
for (j = 0; j < WELL_WIDTH; j++)
temp_base[j][i] = 0;
/* Find pieces and overlap them if necessary */
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape
[j * tp->curPolyomino.size + i]) {
xi = (tp->curPolyomino.xpos + i) % WELL_PERIMETER;
yj = tp->curPolyomino.ypos + j;
if (yj >= WELL_DEPTH) {
wall_to_base(&base_i, &base_j, xi, yj);
xi = checkWall(rotateIndex(curPixmap(tp, i, j), xi),
base_i, base_j);
if (!temp_base[base_j][base_i])
temp_base[base_j][base_i] = xi;
else
temp_base[base_j][base_i] &= xi;
}
}
/* Now draw */
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape
[j * tp->curPolyomino.size + i]) {
xi = (tp->curPolyomino.xpos + i) % WELL_PERIMETER;
yj = tp->curPolyomino.ypos + j;
if (yj >= WELL_DEPTH) {
wall_to_base(&base_i, &base_j, xi, yj);
if (temp_base[base_j][base_i] > 0) {
drawSquare(mi, temp_base[base_j][base_i],
tp->curPolyomino.color_number, base_i, base_j);
/* negate for erase */
temp_base[base_j][base_i] = -temp_base[base_j][base_i];
}
} else if (yj >= 0) {
drawTrapazoid(mi, /* tp->curPolyomino.pmid, */
tp->curPolyomino.color_number, xi, yj);
}
}
/* Erase */
for (i = 0; i < old->size; i++)
for (j = 0; j < old->size; j++) {
xi = (old->xpos + i) % WELL_PERIMETER;
yj = old->ypos + j;
ox = (xi - tp->curPolyomino.xpos + WELL_PERIMETER) % WELL_PERIMETER;
oy = yj - tp->curPolyomino.ypos;
if (yj >= WELL_DEPTH) {
wall_to_base(&base_i, &base_j, xi, yj);
if (polytris[(int) tp->bonusNow].polyomino
[old->polyomino_number].shape
[j * old->size + i] &&
((ox >= tp->curPolyomino.size) || (oy < 0) ||
(oy >= tp->curPolyomino.size) || !temp_base[base_j][base_i]))
clearSquare(mi, base_i, base_j);
} else if (yj >= 0) {
if (polytris[(int) tp->bonusNow].polyomino
[old->polyomino_number].shape
[j * old->size + i] &&
((ox >= tp->curPolyomino.size) || (oy < 0) ||
(oy >= tp->curPolyomino.size) || !polytris
[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape
[oy * tp->curPolyomino.size + ox]))
clearTrapazoid(mi, xi, yj);
}
}
} else {
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if ((tp->curPolyomino.ypos + j >= 0) &&
polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
drawSquare(mi, curPixmap(tp, i, j),
tp->curPolyomino.color_number,
tp->curPolyomino.xpos + i, tp->curPolyomino.ypos + j);
}
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++) {
ox = old->xpos + i - tp->curPolyomino.xpos;
oy = old->ypos + j - tp->curPolyomino.ypos;
if (polytris[(int) tp->bonusNow].polyomino[old->polyomino_number].shape[j * tp->curPolyomino.size + i] &&
((ox < 0) || (ox >= tp->curPolyomino.size) ||
(oy < 0) || (oy >= tp->curPolyomino.size) ||
!polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[oy * tp->curPolyomino.size + ox])) {
clearSquare(mi, (old->xpos + i), (old->ypos + j));
}
}
}
}
static void
redoNext(ModeInfo * mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int next_start, i;
next_start = (int) (tp->curPolyomino.random_number % polytris[(int) tp->bonusNow].mode.number);
tp->curPolyomino.color_number = next_start;
tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].mode.start[next_start];
for (i = 0; i < tp->curPolyomino.random_rotation; i++)
tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].rotation;
tp->curPolyomino.size = polytris[(int) tp->bonusNow].polyomino[tp->curPolyomino.polyomino_number].size;
tp->curPolyomino.ypos = -polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].leadingEmptyHeight;
}
static void
newPolyomino(ModeInfo * mi) {
trisstruct *tp = &triss[MI_SCREEN(mi)];
tp->curPolyomino.random_number = LRAND();
tp->curPolyomino.random_rotation = NRAND(MAX_SIDES);
#if 0
tp->curPolyomino.random_reflection = NRAND(2); /* Future? */
#endif
if (tp->bonus) {
if (trackmouse) {
tp->bonusNow = 0;
} else {
/* Unlikely to finish rows, so show bonus pieces this way */
tp->bonusNow = !NRAND(16);
}
}
redoNext(mi);
if (tp->well) {
int w;
/* pick a free spot on the rim, not all frozen else this is an infinite
loop */
/* tp->curPolyomino.xpos = (WELL_WIDTH - tp->curPolyomino.size) / 2; */
tp->curPolyomino.xpos = NRAND(WELL_WIDTH - tp->curPolyomino.size + 1);
do {
w = NRAND(MAX_SIDES);
} while (tp->frozen_wall[w]);
tp->curPolyomino.xpos += w * WELL_WIDTH;
} else {
tp->curPolyomino.xpos = NRAND(tp->ncols - tp->curPolyomino.size + 1);
/* tp->curPolyomino.xpos = (tp->ncols - tp->curPolyomino.size) / 2; */
}
}
static void
putBox(ModeInfo * mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j;
int x = tp->curPolyomino.xpos, y = tp->curPolyomino.ypos, pos;
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if ((y + j >= 0) && polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
if (tp->well) {
/* Set pieces and overlap them if necessary */
int xi, yj;
xi = (x + i) % WELL_PERIMETER;
yj = y + j;
if (yj >= WELL_DEPTH) {
int base_i, base_j;
wall_to_base(&base_i, &base_j, xi, yj);
xi = checkWall(rotateIndex(curPixmap(tp, i, j), xi),
base_i, base_j);
if (!tp->base[base_j][base_i].pmid)
tp->base[base_j][base_i].pmid = xi;
else
tp->base[base_j][base_i].pmid &= xi;
tp->base[base_j][base_i].cid = tp->curPolyomino.color_number;
} else {
tp->wall[yj][xi].pmid =
checkSides(checkBottom(rotateIndex(curPixmap(tp, i,
j), xi), xi, yj), xi);
tp->wall[yj][xi].cid = tp->curPolyomino.color_number;
tp->frozen_wall[xi / WELL_WIDTH] = MAX_SIDES;
}
} else {
pos = (y + j) * tp->ncols + x + i;
tp->field[pos].pmid = curPixmap(tp, i, j);
tp->field[pos].cid = tp->curPolyomino.color_number;
}
}
}
static Bool
overlapping(ModeInfo * mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j;
int x = tp->curPolyomino.xpos, y = tp->curPolyomino.ypos;
Bool gradualAppear = tp->well;
if (tp->well) {
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
int xi, yj;
xi = (x + i) % WELL_PERIMETER;
yj = y + j;
if (yj < WELL_DEPTH) {
if (tp->frozen_wall[xi / WELL_WIDTH])
return True;
if (gradualAppear) {
/* This method one can turn polyomino to an area above of screen, also
part of the polyomino may not be visible initially */
if ((yj >= 0) && (tp->wall[yj][xi].pmid >= 0))
return True;
} else {
/* This method does not allow turning polyomino to an area above screen */
if ((yj < 0) || (tp->wall[yj][xi].pmid >= 0))
return True;
}
}
else if (yj < WELL_DEPTH + WELL_WIDTH) {
int base_i, base_j;
wall_to_base(&base_i, &base_j, xi, yj);
if (tp->base[base_j][base_i].pmid >= 0)
return True;
}
else
return True;
}
} else {
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
if ((y + j >= tp->nrows) || (x + i < 0) || (x + i >= tp->ncols))
return True;
if (gradualAppear) {
/* This method one can turn polyomino to an area above of screen, also
part of the polyomino may not be visible initially */
if ((y + j >= 0) &&
(tp->field[(y + j) * tp->ncols + x + i].pmid >= 0))
return True;
} else {
/* This method does not allow turning polyomino to an area above screen */
if ((y + j < 0) ||
(tp->field[(y + j) * tp->ncols + x + i].pmid >= 0))
return True;
}
}
}
return False;
}
static Bool
atBottom(ModeInfo * mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j;
int x = tp->curPolyomino.xpos, y = tp->curPolyomino.ypos;
if (tp->well) {
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
int xi, yj;
xi = (x + i) % WELL_PERIMETER;
yj = y + j;
if (yj < -1)
return False;
if (yj < WELL_DEPTH - 1) {
if (tp->frozen_wall[xi / WELL_WIDTH])
return True;
if (tp->wall[yj + 1][xi].pmid >= 0)
return True;
} else if (yj < WELL_DEPTH + WELL_WIDTH - 1) {
int base_i, base_j;
wall_to_base(&base_i, &base_j, xi, yj + 1);
if (tp->base[base_j][base_i].pmid >= 0)
return True;
} else
return True;
}
} else {
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if ((y + j >= -1) && polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i])
if ((y + j >= tp->nrows - 1) || (x + i < 0) || (x + i >= tp->ncols) ||
(tp->field[(y + j + 1) * tp->ncols + x + i].pmid >= 0))
return True;
}
return False;
}
static Bool
atBaseFully(ModeInfo * mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j;
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if (tp->curPolyomino.ypos + j < WELL_DEPTH &&
polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i])
return False;
return True;
}
static Bool
atBasePartially(ModeInfo * mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j;
for (i = 0; i < tp->curPolyomino.size; i++)
for (j = 0; j < tp->curPolyomino.size; j++)
if ((tp->curPolyomino.ypos + j >= WELL_DEPTH) &&
polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i])
return True;
return False;
}
static Bool
wallChange(ModeInfo *mi, thing_t oldthing, thing_t newthing)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
int w = -1, i, j;
int x = tp->curPolyomino.xpos;
for (i = 0; i < oldthing.size; i++)
for (j = 0; j < oldthing.size; j++)
if (polytris[(int) tp->bonusNow].polyomino[oldthing.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
if (w == -1)
w = ((x + i) % WELL_PERIMETER) / WELL_WIDTH;
else if (w != ((x + i) % WELL_PERIMETER) / WELL_WIDTH)
return True;
}
for (i = 0; i < newthing.size; i++)
for (j = 0; j < newthing.size; j++)
if (polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
if (w != ((x + i) % WELL_PERIMETER) / WELL_WIDTH)
return True;
}
return False;
}
static void
tryMove(ModeInfo * mi, move_t move)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
thing_t old;
int i;
int skip = False;
int cw = False; /* I am not sure if this is the original */
old = tp->curPolyomino;
switch (move) {
case FALL:
tp->curPolyomino.ypos++;
break;
case DROP:
do { /* so fast you can not see it ;) */
tp->curPolyomino.ypos ++;
} while (!overlapping(mi));
tp->curPolyomino.ypos--;
break;
case MOVE_LEFT:
if (tp->well) {
tp->curPolyomino.xpos = (tp->curPolyomino.xpos + WELL_PERIMETER - 1) % WELL_PERIMETER;
if (atBaseFully(mi) || (atBasePartially(mi) && wallChange(mi, old, tp->curPolyomino)))
skip = True;
} else
tp->curPolyomino.xpos--;
break;
case MOVE_RIGHT:
if (tp->well) {
tp->curPolyomino.xpos = (tp->curPolyomino.xpos + WELL_PERIMETER + 1) % WELL_PERIMETER;
if (atBaseFully(mi) || (atBasePartially(mi) && wallChange(mi, old, tp->curPolyomino)))
skip = True;
} else
tp->curPolyomino.xpos++;
break;
case ROTATE:
if (cw)
for (i = 0; i < MAX_SIDES - 1; i++)
tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].rotation;
else /* ccw */
tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].rotation;
tp->curPolyomino.xpos = old.xpos;
tp->curPolyomino.ypos = old.ypos;
if (tp->well && atBasePartially(mi) && wallChange(mi, old, tp->curPolyomino))
skip = True;
break;
case REFLECT: /* reflect on y axis */
tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].polyomino
[tp->curPolyomino.polyomino_number].reflection;
tp->curPolyomino.xpos = old.xpos;
tp->curPolyomino.ypos = old.ypos;
if (tp->well && atBasePartially(mi) && wallChange(mi, old, tp->curPolyomino))
skip = True;
break;
}
if (!skip && !overlapping(mi)) {
drawPolyominoDiff(mi, &old);
} else {
tp->curPolyomino = old;
tp->sidemoves = tp->ncols / 2;
}
}
static void
fillLines(ModeInfo *mi)
{
Display *display = MI_DISPLAY(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
int i, j;
XFlush(display);
if (tp->well) {
for (j = 0; j < WELL_DEPTH + WELL_WIDTH; j++)
for (i = 0; i < WELL_PERIMETER; i++) {
drawBox(mi, BITMAPS - 1,
LRAND() % polytris[(int) tp->bonusNow].mode.number,
i, j);
XFlush(display);
}
} else {
for (j = 0; j <= tp->nrows / 2; j++) {
for (i = 0; i < tp->ncols; i++) {
drawSquare(mi, BITMAPS - 1,
LRAND() % polytris[(int) tp->bonusNow].mode.number,
i, j);
drawSquare(mi, BITMAPS - 1,
LRAND() % polytris[(int) tp->bonusNow].mode.number,
i, tp->nrows - j - 1);
}
XFlush(display);
}
}
}
static void
free_images(trisstruct *tp)
{
int n;
if (tp->images[BITMAPS - 1] != None) {
(void) XDestroyImage(tp->images[BITMAPS - 1]);
tp->images[BITMAPS - 1] = None;
}
for (n = 0; n < BITMAPS - 1; n++) {
/* Don't bother to free duplicates */
if (IS_UP(n) && IS_DOWN(n) && IS_LEFT(n) && IS_RIGHT(n))
tp->images[n] = None;
else if (IS_LEFT_UP(n) && (IS_LEFT(n) || IS_UP(n)))
tp->images[n] = None;
else if (IS_LEFT_DOWN(n) && (IS_LEFT(n) || IS_DOWN(n)))
tp->images[n] = None;
else if (IS_RIGHT_UP(n) && (IS_RIGHT(n) || IS_UP(n)))
tp->images[n] = None;
else if (IS_RIGHT_DOWN(n) && (IS_RIGHT(n) || IS_DOWN(n)))
tp->images[n] = None;
else if (tp->images[n] != None) {
(void) XDestroyImage(tp->images[n]);
tp->images[n] = None;
}
}
}
static void
free_tetris(Display *display, trisstruct *tp)
{
ModeInfo *mi = tp->mi;
if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
MI_WHITE_PIXEL(mi) = tp->whitepixel;
MI_BLACK_PIXEL(mi) = tp->blackpixel;
#ifndef STANDALONE
MI_FG_PIXEL(mi) = tp->fg;
MI_BG_PIXEL(mi) = tp->bg;
#endif
if (tp->colors != NULL) {
if (tp->ncolors && !tp->no_colors)
free_colors(display, tp->cmap, tp->colors, tp->ncolors);
free(tp->colors);
tp->colors = (XColor *) NULL;
}
if (tp->cmap != None) {
XFreeColormap(display, tp->cmap);
tp->cmap = None;
}
}
if (tp->gc != None) {
XFreeGC(display, tp->gc);
tp->gc = None;
}
if (tp->graypix != None) {
XFreePixmap(display, tp->graypix);
tp->graypix = None;
}
if (tp->field != NULL) {
free(tp->field);
tp->field = (fieldstruct *) NULL;
}
free_images(tp);
}
static int
checkLines(ModeInfo *mi)
{
trisstruct *tp = &triss[MI_SCREEN(mi)];
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
if (tp->well) {
int lSet[2][WELL_WIDTH], nset[2];
int i, j, k, *tmp;
nset[0] = nset[1] = 0;
/* Find filled rows */
for (j = 0; j < WELL_WIDTH; j++) {
lSet[0][j] = 0;
for (i = 0; i < WELL_WIDTH; i++)
if (tp->base[j][i].pmid >= 0)
lSet[0][j] ++;
if (lSet[0][j] == WELL_WIDTH)
nset[0] ++;
}
/* Find filled columns */
for (i = 0; i < WELL_WIDTH; i++) {
lSet[1][i] = 0;
for (j = 0; j < WELL_WIDTH; j++)
if (tp->base[j][i].pmid >= 0)
lSet[1][i] ++;
if (lSet[1][i] == WELL_WIDTH)
nset[1] ++;
}
if (nset[0] || nset[1]) {
fieldstruct temp_base[WELL_WIDTH][WELL_WIDTH];
for (k = 0; k < ((NUM_FLASHES / (nset[0] + nset[1])) / 2) * 2; k++) {
for (j = 0; j < WELL_WIDTH; j++) {
if (lSet[0][j] == WELL_WIDTH)
for (i = 0; i < WELL_WIDTH; i++)
xorSquare(mi, i, j);
if (lSet[1][j] == WELL_WIDTH)
for (i = 0; i < WELL_WIDTH; i++)
xorSquare(mi, j, i);
}
XFlush(display);
}
for (j = 0; j < WELL_WIDTH; j++)
for (i = 0; i < WELL_WIDTH; i++)
temp_base[j][i] = tp->base[j][i];
if (nset[0]) {
for (j = 0; j < WELL_WIDTH / 2; j++)
if (lSet[0][j] == WELL_WIDTH)
for (i = 0; i < WELL_WIDTH; i++) {
for (k = j; k > 0; k--)
temp_base[k][i] = temp_base[k - 1][i];
temp_base[0][i].pmid = EMPTY;
if (j > 0) {
tmp = &temp_base[j][i].pmid;
if (*tmp >= 0)
*tmp = CHECKDOWN((unsigned int) *tmp);
}
tmp = &temp_base[j + 1][i].pmid;
if (*tmp >= 0)
*tmp = CHECKUP((unsigned int) *tmp);
}
for (j = WELL_WIDTH - 1; j >= WELL_WIDTH / 2; j--)
if (lSet[0][j] == WELL_WIDTH)
for (i = 0; i < WELL_WIDTH; i++) {
for (k = j; k < WELL_WIDTH - 1; k++)
temp_base[k][i] = temp_base[k + 1][i];
temp_base[WELL_WIDTH - 1][i].pmid = EMPTY;
if (j < WELL_WIDTH - 1) {
tmp = &temp_base[j][i].pmid;
if (*tmp >= 0)
*tmp = CHECKUP((unsigned int) *tmp);
}
tmp = &temp_base[j - 1][i].pmid;
if (*tmp >= 0)
*tmp = CHECKDOWN((unsigned int) *tmp);
}
}
if (nset[1]) {
for (i = 0; i < WELL_WIDTH / 2; i++)
if (lSet[1][i] == WELL_WIDTH)
for (j = 0; j < WELL_WIDTH; j++) {
for (k = i; k > 0; k--)
temp_base[j][k] = temp_base[j][k - 1];
temp_base[j][0].pmid = EMPTY;
if (i > 0) {
tmp = &temp_base[j][i].pmid;
if (*tmp >= 0)
*tmp = CHECKRIGHT((unsigned int) *tmp);
}
tmp = &temp_base[j][i + 1].pmid;
if (*tmp >= 0)
*tmp = CHECKLEFT((unsigned int) *tmp);
}
for (i = WELL_WIDTH - 1; i >= WELL_WIDTH / 2; i--)
if (lSet[1][i] == WELL_WIDTH)
for (j = 0; j < WELL_WIDTH; j++) {
for (k = i; k < WELL_WIDTH - 1; k++)
temp_base[j][k] = temp_base[j][k + 1];
temp_base[j][WELL_WIDTH - 1].pmid = EMPTY;
if (i < WELL_WIDTH - 1) {
tmp = &temp_base[j][i].pmid;
if (*tmp >= 0)
*tmp = CHECKLEFT((unsigned int) *tmp);
}
tmp = &temp_base[j][i - 1].pmid;
if (*tmp >= 0)
*tmp = CHECKRIGHT((unsigned int) *tmp);
}
}
for (j = 0; j < WELL_WIDTH; j++)
for (i = 0; i < WELL_WIDTH; i++)
if ((temp_base[j][i].cid != tp->base[j][i].cid) ||
(temp_base[j][i].pmid != tp->base[j][i].pmid))
{
tp->base[j][i] = temp_base[j][i];
if (tp->base[j][i].pmid >= 0)
drawSquare(mi, tp->base[j][i].pmid, tp->base[j][i].cid, i, j);
else
clearSquare(mi, i, j);
}
XFlush(display);
}
return (nset[0] + nset[1]);
} else {
int *lSet, nset = 0;
int i, j, y;
if ((lSet = (int *) calloc(tp->nrows, sizeof (int))) == NULL) {
free_tetris(display, tp);
return -1; /* error */
}
for (j = 0; j < tp->nrows; j++) {
for (i = 0; i < tp->ncols; i++)
if (tp->field[j * tp-> ncols + i].pmid >= 0)
lSet[j]++;
if (lSet[j] == tp->ncols)
nset++;
}
if (nset) {
for (i = 0; i < ((NUM_FLASHES / nset) % 2) * 2; i++) {
for (j = 0; j < tp->nrows; j++) {
if (lSet[j] == tp->ncols)
for (i = 0; i < tp->ncols; i++)
xorSquare(mi, i, j);
}
XFlush(display);
}
for (j = tp->nrows - 1; j >= 0; j--) {
if (lSet[j] == tp->ncols) {
for (y = j; y > 0; y--)
for (i = 0; i < tp->ncols; i++)
tp->field[y * tp->ncols + i] =
tp->field[(y - 1) * tp->ncols + i];
for (i = 0; i < tp->ncols; i++)
tp->field[i].pmid = EMPTY;
XCopyArea(display, window, window, tp->gc,
tp->xb, tp->yb,
tp->xs * tp->ncols, j * tp->ys,
tp->xb, tp->yb + tp->ys);
XSetForeground(display, tp->gc, tp->blackpixel);
XFillRectangle(display, window, tp->gc,
tp->xb, tp->yb, tp->xs * tp->ncols, tp->ys);
for (i = j; i > 0; i--)
lSet[i] = lSet[i - 1];
lSet[0] = 0;
if (j > 0)
for (i = 0; i < tp->ncols; i++) {
int tmp = tp->field[j * tp->ncols + i].pmid;
if ((tmp >= 0) && (tmp != CHECKDOWN(tmp))) {
tp->field[j * tp->ncols + i].pmid = CHECKDOWN(tmp);
drawSquare(mi,
tp->field[j * tp->ncols + i].pmid, tp->field[j * tp->ncols + i].cid,
i, j);
}
}
j++;
if (j < tp->nrows)
for (i = 0; i < tp->ncols; i++) {
int tmp = tp->field[j * tp->ncols + i].pmid;
if ((tmp >= 0) && (tmp != CHECKUP(tmp))) {
tp->field[j * tp->ncols + i].pmid = CHECKUP(tmp);
drawSquare(mi,
tp->field[j * tp->ncols + i].pmid, tp->field[j * tp->ncols + i].cid,
i, j);
}
}
XFlush(display);
}
}
}
if (lSet)
free(lSet);
return nset;
}
}
static void
gameOver(ModeInfo *mi)
{
/* maybe more stuff later */
fillLines(mi);
}
static Bool
create_an_image(ModeInfo *mi, trisstruct *tp, int n)
{
int x, y;
unsigned char * data;
if ((data = (unsigned char *) malloc(sizeof(char)*(tp->ys*ROUND8(tp->ys)/8))) == NULL) {
return False; /* no biggie, will not use images */
}
for (y=0;y<tp->ys;y++) for (x=0;x<tp->ys;x++) {
if (!tp->use3D) {
#ifdef SMALL_BELLYBUTTON
if (x >= tp->ys / 2 && x <= tp->ys / 2 + 1 &&
y >= tp->ys / 2 && y <= tp->ys / 2 + 1)
NOTHALFBIT(x,y)
else
#endif
HALFBIT(x,y)
} else if ((x>=y && x<=tp->ys-y-1 && IS_UP(n))
|| (x<=y && x<=tp->ys-y-1 && y<tp->ys/2 && !IS_LEFT(n))
|| (x>=y && x>=tp->ys-y-1 && y<tp->ys/2 && !IS_RIGHT(n)))
SETBIT(x,y)
else if ((x<=y && x<=tp->ys-y-1 && IS_LEFT(n))
|| (x>=y && x<=tp->ys-y-1 && x<tp->ys/2 && !IS_UP(n))
|| (x<=y && x>=tp->ys-y-1 && x<tp->ys/2 && !IS_DOWN(n)))
TWOTHIRDSBIT(x,y)
else if ((x>=y && x>=tp->ys-y-1 && IS_RIGHT(n))
|| (x>=y && x<=tp->ys-y-1 && x>=tp->ys/2 && !IS_UP(n))
|| (x<=y && x>=tp->ys-y-1 && x>=tp->ys/2 && !IS_DOWN(n)))
HALFBIT(x,y)
else if ((x<=y && x>=tp->ys-y-1 && IS_DOWN(n))
|| (x<=y && x<=tp->ys-y-1 && y>=tp->ys/2 && !IS_LEFT(n))
|| (x>=y && x>=tp->ys-y-1 && y>=tp->ys/2 && !IS_RIGHT(n)))
THIRDBIT(x,y)
}
if (IS_LEFT(n))
for (y=0;y<tp->ys;y++) for (x=G;x<G+T;x++)
SETBIT(x,y)
if (IS_RIGHT(n))
for (y=0;y<tp->ys;y++) for (x=G;x<G+T;x++)
SETBIT(tp->ys-1-x,y)
if (IS_UP(n))
for (x=0;x<tp->ys;x++) for (y=G;y<G+T;y++)
SETBIT(x,y)
if (IS_DOWN(n))
for (x=0;x<tp->ys;x++) for (y=G;y<G+T;y++)
SETBIT(x,tp->ys-1-y)
if (IS_LEFT(n))
for (y=0;y<tp->ys;y++) for (x=0;x<G;x++)
RESBIT(x,y)
if (IS_RIGHT(n))
for (y=0;y<tp->ys;y++) for (x=0;x<G;x++)
RESBIT(tp->ys-1-x,y)
if (IS_UP(n))
for (x=0;x<tp->ys;x++) for (y=0;y<G;y++)
RESBIT(x,y)
if (IS_DOWN(n))
for (x=0;x<tp->ys;x++) for (y=0;y<G;y++)
RESBIT(x,tp->ys-1-y)
if (IS_LEFT(n) && IS_UP(n))
for (x=G;x<=G+R;x++)
for (y=G;y<=R+2*G-x;y++) {
if (x+y>R+2*G-RT)
SETBIT(x,y)
else
RESBIT(x,y)
}
if (IS_LEFT(n) && IS_DOWN(n))
for (x=G;x<=G+R;x++)
for (y=G;y<=R+2*G-x;y++) {
if (x+y>R+2*G-RT)
SETBIT(x,tp->ys-1-y)
else
RESBIT(x,tp->ys-1-y)
}
if (IS_RIGHT(n) && IS_UP(n))
for (x=G;x<=G+R;x++)
for (y=G;y<=R+2*G-x;y++) {
if (x+y>R+2*G-RT)
SETBIT(tp->ys-1-x,y)
else
RESBIT(tp->ys-1-x,y)
}
if (IS_RIGHT(n) && IS_DOWN(n))
for (x=G;x<=G+R;x++)
for (y=G;y<=R+2*G-x;y++) {
if (x+y>R+2*G-RT)
SETBIT(tp->ys-1-x,tp->ys-1-y)
else
RESBIT(tp->ys-1-x,tp->ys-1-y)
}
if (!IS_LEFT(n) && !IS_UP(n) && IS_LEFT_UP(n)) {
for (x=0;x<G;x++) for(y=0;y<G;y++)
RESBIT(x,y)
for (x=G;x<G+T;x++) for(y=0;y<G;y++)
SETBIT(x,y)
for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
SETBIT(x,y)
}
if (!IS_LEFT(n) && !IS_DOWN(n) && IS_LEFT_DOWN(n)) {
for (x=0;x<G;x++) for(y=0;y<G;y++)
RESBIT(x,tp->ys-1-y)
for (x=G;x<G+T;x++) for(y=0;y<G;y++)
SETBIT(x,tp->ys-1-y)
for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
SETBIT(x,tp->ys-1-y)
}
if (!IS_RIGHT(n) && !IS_UP(n) && IS_RIGHT_UP(n)) {
for (x=0;x<G;x++) for(y=0;y<G;y++)
RESBIT(tp->ys-1-x,y)
for (x=G;x<G+T;x++) for(y=0;y<G;y++)
SETBIT(tp->ys-1-x,y)
for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
SETBIT(tp->ys-1-x,y)
}
if (!IS_RIGHT(n) && !IS_DOWN(n) && IS_RIGHT_DOWN(n)) {
for (x=0;x<G;x++) for(y=0;y<G;y++)
RESBIT(tp->ys-1-x,tp->ys-1-y)
for (x=G;x<G+T;x++) for(y=0;y<G;y++)
SETBIT(tp->ys-1-x,tp->ys-1-y)
for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
SETBIT(tp->ys-1-x,tp->ys-1-y)
}
#ifdef LARGE_BELLYBUTTON
if (!tp->use3D) {
if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n))
for (x=0;x<G+T;x++) for(y=0;y<G+T;y++)
SETBIT(x,y)
if (!IS_LEFT(n) && !IS_DOWN(n) && !IS_LEFT_DOWN(n))
for (x=0;x<G+T;x++) for(y=tp->ys-G-T;y<tp->ys;y++)
SETBIT(x,y)
if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n))
for (x=tp->ys-G-T;x<tp->ys;x++) for(y=0;y<G+T;y++)
SETBIT(x,y)
if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n))
for (x=tp->ys-G-T;x<tp->ys;x++) for(y=tp->ys-G-T;y<tp->ys;y++)
SETBIT(x,y)
} else
#else
if (tp->use3D)
#endif
{
if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n))
for (x=0;x<tp->ys/2-RR;x++) for(y=0;y<tp->ys/2-RR;y++)
THREEQUARTERSBIT(x,y)
if (!IS_LEFT(n) && !IS_DOWN(n) && !IS_LEFT_DOWN(n))
for (x=0;x<tp->ys/2-RR;x++) for(y=tp->ys/2+RR;y<tp->ys;y++)
THREEQUARTERSBIT(x,y)
if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n))
for (x=tp->ys/2+RR;x<tp->ys;x++) for(y=0;y<tp->ys/2-RR;y++)
THREEQUARTERSBIT(x,y)
if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n))
for (x=tp->ys/2+RR;x<tp->ys;x++) for(y=tp->ys/2+RR;y<tp->ys;y++)
THREEQUARTERSBIT(x,y)
}
if ((tp->images[n] = XCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi),
1, XYBitmap, 0, (char *) data, tp->xs, tp->ys, 8, 0)) == None) {
free(data);
return False;
}
tp->images[n]->byte_order = MSBFirst;
tp->images[n]->bitmap_unit = 8;
tp->images[n]->bitmap_bit_order = LSBFirst;
return True;
}
static Bool
create_images(ModeInfo *mi, trisstruct *tp)
{
int n;
(void) create_an_image(mi, tp, BITMAPS - 1);
for (n = 0; n < BITMAPS - 1; n++) {
/* Avoid duplication of identical images. */
if (IS_UP(n) && IS_DOWN(n) && IS_LEFT(n) && IS_RIGHT(n))
tp->images[n] = tp->images[BITMAPS - 1];
else if (IS_LEFT_UP(n) && (IS_LEFT(n) || IS_UP(n)))
tp->images[n] = tp->images[n & ~LEFT_UP];
else if (IS_LEFT_DOWN(n) && (IS_LEFT(n) || IS_DOWN(n)))
tp->images[n] = tp->images[n & ~LEFT_DOWN];
else if (IS_RIGHT_UP(n) && (IS_RIGHT(n) || IS_UP(n)))
tp->images[n] = tp->images[n & ~RIGHT_UP];
else if (IS_RIGHT_DOWN(n) && (IS_RIGHT(n) || IS_DOWN(n)))
tp->images[n] = tp->images[n & ~RIGHT_DOWN];
else if (image_needed(n)) {
if (!create_an_image(mi, tp, n))
return False;
}
}
return True;
}
void
refresh_tetris(ModeInfo * mi)
{
trisstruct *tp;
if (triss == NULL)
return;
tp = &triss[MI_SCREEN(mi)];
if (!tp->well && tp->field == NULL)
return;
if (!tp->painted)
return;
MI_CLEARWINDOW(mi);
if (tp->well) {
int i, j;
for (i = 0; i < WELL_PERIMETER; i++) {
for (j = 0; j < WELL_DEPTH; j++) {
if (tp->wall[j][i].pmid >= 0) {
drawTrapazoid(mi, /* tp->wall[j][i].pmid, */ tp->wall[j][i].cid, i, j);
}
if (tp->frozen_wall[i / WELL_WIDTH]) {
freezeTrapazoid(mi, i, j);
}
}
}
for (i = 0; i < WELL_WIDTH; i++)
for (j = 0; j < WELL_WIDTH; j++)
if (tp->base[j][i].pmid >= 0)
drawSquare(mi, tp->base[j][i].pmid, tp->base[j][i].cid, i, j);
} else {
int col, row, pos;
for (col = 0; col < tp->ncols; col++)
for (row = 0; row < tp->nrows; row++) {
pos = row * tp->ncols + col;
#ifdef DEBUG
(void) printf("pmid %d, cid %d, %d %d\n",
tp->field[pos].pmid, tp->field[pos].cid, col, row);
#endif
if (tp->field[pos].pmid != EMPTY)
drawSquare(mi, tp->field[pos].pmid, tp->field[pos].cid, col, row);
}
}
drawPolyomino(mi);
}
void
release_tetris(ModeInfo * mi) {
int bonustype, number;
if (triss != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_tetris(MI_DISPLAY(mi), &triss[screen]);
free(triss);
triss = (trisstruct *) NULL;
for (bonustype = 0; bonustype < 2; bonustype++) {
if (polytris[bonustype].polyomino != NULL) {
for (number = 0; number < max_ominoes[bonustype]; number++) {
if (polytris[bonustype].polyomino[number].shape != NULL)
free(polytris[bonustype].polyomino[number].shape);
}
free(polytris[bonustype].polyomino);
polytris[bonustype].polyomino = (Polyominoes *) NULL;
}
if (polytris[bonustype].mode.start != NULL) {
free(polytris[bonustype].mode.start);
polytris[bonustype].mode.start = (int *) NULL;
}
}
}
}
void
change_tetris(ModeInfo * mi) {
trisstruct *tp;
if (triss == NULL)
return;
tp = &triss[MI_SCREEN(mi)];
if (!tp->well && tp->field == NULL)
return;
tryMove(mi, ROTATE);
}
#ifndef STANDALONE
extern char *background;
extern char *foreground;
#endif
void
init_tetris(ModeInfo * mi) {
Display * display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
int size = MI_SIZE(mi);
int i, j, bonustype;
trisstruct *tp;
if (triss == NULL) {
if ((triss = (trisstruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (trisstruct))) == NULL)
return;
for (bonustype = 0; bonustype < 2; bonustype++) {
if (((polytris[bonustype].mode.start = (int *) malloc(start_ominoes[bonustype] *
sizeof(int))) == NULL) ||
((polytris[bonustype].polyomino = (Polyominoes *) malloc(max_ominoes[bonustype] *
sizeof(Polyominoes))) == NULL)) {
release_tetris(mi);
return;
}
}
if (!readPolyominoes()) {
release_tetris(mi);
return;
}
#ifdef DEBUG
writePolyominoes(tp);
#endif
}
tp = &triss[MI_SCREEN(mi)];
tp->mi = mi;
if (MI_IS_FULLRANDOM(mi)) {
tp->bonus = (Bool) (LRAND() & 1);
tp->well = !(NRAND(3));
tp->use3D = (Bool) (NRAND(4));
} else {
tp->bonus = bonus;
tp->well = well;
tp->use3D = !plain;
}
if (tp->gc == None) {
tp->blackpixel = MI_BLACK_PIXEL(mi);
if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
XColor color;
#ifndef STANDALONE
tp->fg = MI_FG_PIXEL(mi);
tp->bg = MI_BG_PIXEL(mi);
#endif
tp->whitepixel = MI_WHITE_PIXEL(mi);
if ((tp->cmap = XCreateColormap(display, window,
MI_VISUAL(mi), AllocNone)) == None) {
free_tetris(display, tp);
return;
}
XSetWindowColormap(display, window, tp->cmap);
(void) XParseColor(display, tp->cmap, "black", &color);
(void) XAllocColor(display, tp->cmap, &color);
MI_BLACK_PIXEL(mi) = color.pixel;
(void) XParseColor(display, tp->cmap, "white", &color);
(void) XAllocColor(display, tp->cmap, &color);
MI_WHITE_PIXEL(mi) = color.pixel;
#ifndef STANDALONE
(void) XParseColor(display, tp->cmap, background, &color);
(void) XAllocColor(display, tp->cmap, &color);
MI_BG_PIXEL(mi) = color.pixel;
(void) XParseColor(display, tp->cmap, foreground, &color);
(void) XAllocColor(display, tp->cmap, &color);
MI_FG_PIXEL(mi) = color.pixel;
#endif
tp->colors = (XColor *) NULL;
tp->ncolors = 0;
}
if ((tp->gc = XCreateGC(display, MI_WINDOW(mi),
(unsigned long) 0, (XGCValues *) NULL)) == None) {
free_tetris(display, tp);
return;
}
#if 0
{
XGCValues gcv;
gcv.function = GXxor;
gcv.foreground = tp->fg;
gcv.background = tp->bg;
}
#endif
}
MI_CLEARWINDOW(mi);
tp->painted = False;
tp->width = MI_WIDTH(mi);
tp->height = MI_HEIGHT(mi);
free_images(tp);
if (tp->width < 2 + 2 * tp->well)
tp->width = 2 + 2 * tp->well;
if (tp->height < 2 + 2 * tp->well)
tp->height = 2 + 2 * tp->well;
if (size >= MAXPIXELSIZE &&
(MINGRIDSIZE * size < tp->width && MINGRIDSIZE * size < tp->height)) {
if (!tp->well) {
tp->xs = size;
tp->ys = size;
} else {
tp->xs = tp->ys = MAX(MINSIZE, MIN(tp->width, tp->height) /
((tp->well) ? 3 * MINGRIDSIZE : MINGRIDSIZE));
}
} else {
if (size < -MINSIZE)
tp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(tp->width, tp->height) /
((tp->well) ? 3 * MINGRIDSIZE : MINGRIDSIZE))) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE)
tp->ys = MINSIZE;
else
tp->ys = MIN(size, MAX(MINSIZE, MIN(tp->width, tp->height) /
((tp->well) ? 3 * MINGRIDSIZE : MINGRIDSIZE)));
tp->xs = tp->ys;
}
if (tp->ys >= MAXPIXELSIZE) {
tp->xs = tp->ys = (tp->ys / 12) * 12;
if (!create_images(mi, tp)) {
free_images(tp);
tp->pixelmode = True;
} else
tp->pixelmode = False;
} else
tp->pixelmode = True;
if (tp->well) {
if ((tp->graypix = XCreateBitmapFromData(display, window,
(char *) gray1_bits, gray1_width, gray1_height)) == None) {
free_tetris(display, tp);
return;
}
tp->ncols = MAX_SIDES * WELL_WIDTH;
tp->nrows = WELL_DEPTH;
tp->xb = tp->width / 2 - tp->xs * WELL_WIDTH / 2;
tp->yb = tp->height / 2 - tp->ys * WELL_WIDTH / 2;
for (i = 0; i < WELL_PERIMETER; i++) {
for (j = 0; j < WELL_DEPTH + WELL_WIDTH; j++) {
tp->wall[j][i].pmid = EMPTY;
tp->wall[j][i].cid = 0;
}
}
for (i = 0; i < WELL_WIDTH; i++) {
for (j = 0; j < WELL_WIDTH; j++) {
tp->base[j][i].pmid = EMPTY;
tp->base[j][i].cid = BITMAPS - 1;
}
}
for (i = 0; i < MAX_SIDES; i++) {
tp->frozen_wall[i] = 0;
}
} else {
tp->ncols = MAX(tp->width / tp->xs, 5);
tp->nrows = MAX(tp->height / tp->ys, 5);
tp->xb = (tp->width - tp->xs * tp->ncols) / 2;
tp->yb = (tp->height - tp->ys * tp->nrows) / 2;
if (tp->field != NULL)
free(tp->field);
if ((tp->field = (fieldstruct *) malloc(tp->ncols * tp->nrows *
sizeof (fieldstruct))) == NULL) {
free_tetris(display, tp);
return;
}
for (i = 0; i < tp->ncols * tp->nrows; i++) {
tp->field[i].pmid = EMPTY;
tp->field[i].cid = 0;
}
}
tp->rows = 0;
tp->level = 0;
tp->bonusNow = False;
/* Set up colour map */
tp->direction = (LRAND() & 1) ? 1 : -1;
if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
if (tp->colors != NULL) {
if (tp->ncolors && !tp->no_colors)
free_colors(display, tp->cmap, tp->colors, tp->ncolors);
free(tp->colors);
tp->colors = (XColor *) NULL;
}
tp->ncolors = MI_NCOLORS(mi);
if (tp->ncolors < 2)
tp->ncolors = 2;
if (tp->ncolors <= 2)
tp->mono_p = True;
else
tp->mono_p = False;
if (tp->mono_p)
tp->colors = (XColor *) NULL;
else
if ((tp->colors = (XColor *) malloc(sizeof (*tp->colors) *
(tp->ncolors + 1))) == NULL) {
free_tetris(display, tp);
return;
}
tp->cycle_p = has_writable_cells(mi);
if (tp->cycle_p) {
if (MI_IS_FULLRANDOM(mi)) {
if (!NRAND(8))
tp->cycle_p = False;
else
tp->cycle_p = True;
} else {
tp->cycle_p = cycle_p;
}
}
if (!tp->mono_p) {
if (!(LRAND() % 10))
make_random_colormap(
#if STANDALONE
display, MI_WINDOW(mi),
#else
mi,
#endif
tp->cmap, tp->colors, &tp->ncolors,
True, True, &tp->cycle_p);
else if (!(LRAND() % 2))
make_uniform_colormap(
#if STANDALONE
display, MI_WINDOW(mi),
#else
mi,
#endif
tp->cmap, tp->colors, &tp->ncolors,
True, &tp->cycle_p);
else
make_smooth_colormap(
#if STANDALONE
display, MI_WINDOW(mi),
#else
mi,
#endif
tp->cmap, tp->colors, &tp->ncolors,
True, &tp->cycle_p);
}
XInstallColormap(display, tp->cmap);
if (tp->ncolors < 2) {
tp->ncolors = 2;
tp->no_colors = True;
} else
tp->no_colors = False;
if (tp->ncolors <= 2)
tp->mono_p = True;
if (tp->mono_p)
tp->cycle_p = False;
}
/* initialize object */
newPolyomino(mi);
}
static Bool
moveOne(ModeInfo *mi, move_t move)
{
Display *display = MI_DISPLAY(mi);
trisstruct *tp = &triss[MI_SCREEN(mi)];
if (move == FALL) {
if (trackmouse) {
tp->sidemoves = tp->sidemoves + 1;
if (tp->sidemoves < tp->ncols / 2) {
Window r, c;
int rx, ry, cx, cy;
unsigned int m;
(void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
&r, &c, &rx, &ry, &cx, &cy, &m);
if (tp->well) {
cx = cx - tp->width / 2;
cy = cy - tp->height / 2;
if (ABS(cx) <= tp->xs * tp->ncols / 2 ||
ABS(cy) <= tp->ys * tp->nrows / 2) {
int ominopos, mousepos, diff;
double wall;
/* Avoid atan2: DOMAIN error message */
if (cy == 0 && cx == 0)
wall = 0.0;
else
wall = 2.0 * (atan2((double) cy, (double) cx) + 3 * M_PI / 4) / M_PI;
if (wall < 0.0)
wall += 4.0;
mousepos = (int) (wall * WELL_WIDTH);
ominopos = tp->curPolyomino.xpos;
diff = mousepos - ominopos;
if (diff < 0)
diff += WELL_WIDTH * MAX_SIDES;
if (diff > WELL_WIDTH * (MAX_SIDES / 2))
move = MOVE_LEFT;
else if (diff > 0)
move = MOVE_RIGHT;
}
} else {
cx = (cx - tp->xb) / tp->xs;
cy = (cy - tp->yb) / tp->ys;
if (!(cx < 0 || cy < 1 || cx >= tp->ncols || cy > tp->nrows)) {
if (cx > tp->curPolyomino.xpos +
polytris[(int) tp->bonusNow].polyomino[tp->curPolyomino.polyomino_number].leadingEmptyWidth) {
move = MOVE_RIGHT;
} else if (cx < tp->curPolyomino.xpos +
polytris[(int) tp->bonusNow].polyomino[tp->curPolyomino.polyomino_number].leadingEmptyWidth) {
move = MOVE_LEFT;
}
}
}
} else {
tp->sidemoves = 0;
}
} else if (!NRAND(4)) {
if (LRAND() & 1)
move = ROTATE;
else if (LRAND() & 1)
move = MOVE_LEFT;
else
move = MOVE_RIGHT;
}
}
if ((move == DROP) || ((move == FALL) && atBottom(mi))) {
putBox(mi);
{
int lines;
if ((lines = checkLines(mi)) < 0) {
return False;
}
tp->rows += lines;
if (tp->rows > THRESHOLD(tp->level)) {
tp->level++;
if (tp->bonus) {
tp->bonusNow = True; /* No good deed goes unpunished */
}
}
}
XFlush(display);
if (tp->well) {
if (allFrozen(mi)) {
gameOver(mi);
init_tetris(mi);
return False;
}
}
newPolyomino(mi);
if (tp->well) {
checkFreeze(mi);
}
if (overlapping(mi)) {
gameOver(mi);
init_tetris(mi);
return False;
}
drawPolyomino(mi);
return True;
} else {
tryMove(mi, move);
if (tp->rows > THRESHOLD(tp->level)) {
tp->level++;
if (tp->bonus) {
tp->bonusNow = True;
}
}
return False;
}
}
void
draw_tetris(ModeInfo * mi)
{
Display * display = MI_DISPLAY(mi);
trisstruct *tp;
if (triss == NULL)
return;
tp = &triss[MI_SCREEN(mi)];
if (!tp->well && tp->field == NULL)
return;
if (tp->no_colors) {
init_tetris(mi);
return;
}
tp->painted = True;
MI_IS_DRAWN(mi) = True;
/* Rotate colours */
if (tp->cycle_p) {
rotate_colors(display, tp->cmap, tp->colors, tp->ncolors,
tp->direction);
if (!(LRAND() % 1000))
tp->direction = -tp->direction;
}
(void) moveOne(mi, FALL);
}
#endif /* MODE_tetris */