syndilights/blib-1.1.7/modules/btetris.c

879 lines
18 KiB
C

/* blib - Library of useful things to hack the Blinkenlights
*
* Copyright (c) 2002 The Blinkenlights Crew
* Daniel Mack <daniel@yoobay.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <glib.h>
#include <gmodule.h>
#include <blib/blib.h>
#define START_SPEED 600
#define ACC_ON_STUCK 4
#define FLASH_SPEED 55
#define FALL_SPEED 50
#define BOTTOM_OFFSET 0 /* we used a value of 2 for Arcade/Paris */
/* #define TETRIS_DEBUG 1 */
#define B_TYPE_TETRIS (b_type_tetris)
#define B_TETRIS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), B_TYPE_TETRIS, BTetris))
#define B_TETRIS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), B_TYPE_TETRIS, BTetrisClass))
#define B_IS_TETRIS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), B_TYPE_TETRIS))
typedef struct _BTetris BTetris;
typedef struct _BTetrisClass BTetrisClass;
struct _BTetris
{
BModule parent_instance;
gint x, y;
gint rotation;
guchar *field;
gint field_size;
gint speed;
gint tile;
gboolean game_over;
gboolean *completed;
gint n_completed;
gboolean falling;
gboolean flash_state;
gint player_device_id;
};
struct _BTetrisClass
{
BModuleClass parent_class;
};
static GType b_tetris_get_type (GTypeModule *module);
static void b_tetris_class_init (BTetrisClass *klass);
static void b_tetris_init (BTetris *tetris);
static gboolean b_tetris_query (gint width,
gint height,
gint channels,
gint maxval);
static gboolean b_tetris_prepare (BModule *module,
GError **error);
static void b_tetris_relax (BModule *module);
static void b_tetris_start (BModule *module);
static void b_tetris_stop (BModule *module);
static void b_tetris_event (BModule *module,
BModuleEvent *event);
static gint b_tetris_tick (BModule *module);
static void b_tetris_describe (BModule *module,
const gchar **title,
const gchar **description,
const gchar **author);
static GType b_type_tetris = 0;
typedef struct _TetrisTile
{
gchar data[4][16];
guchar color;
} TetrisTile;
static TetrisTile tile[] =
{
{
{
{ 0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0 },
{ 0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0 },
},
0x77
},
{
{
{ 1, 1, 0, 0,
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 1, 0, 0,
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 1, 0, 0,
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 1, 0, 0,
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
},
0x99
},
{
{
{ 1, 0, 0, 0,
1, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 1, 0, 0,
1, 0, 0, 0,
1, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 1, 1, 0,
0, 0, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 0, 1, 0, 0,
0, 1, 0, 0,
1, 1, 0, 0,
0, 0, 0, 0 },
},
0xbb
},
{
{
{ 0, 0, 1, 0,
1, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 0, 0, 0,
1, 0, 0, 0,
1, 1, 0, 0,
0, 0, 0, 0 },
{ 1, 1, 1, 0,
1, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 0, 0, 0 },
},
0xbb
},
{
{
{ 1, 1, 1, 0,
0, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 0, 1, 0, 0,
1, 1, 0, 0,
0, 1, 0, 0,
0, 0, 0, 0 },
{ 0, 1, 0, 0,
1, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 0, 0, 0,
1, 1, 0, 0,
1, 0, 0, 0,
0, 0, 0, 0 },
},
0xdd
},
{
{
{ 0, 1, 1, 0,
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 0, 0, 0,
1, 1, 0, 0,
0, 1, 0, 0,
0, 0, 0, 0 },
{ 0, 1, 1, 0,
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 0, 0, 0,
1, 1, 0, 0,
0, 1, 0, 0,
0, 0, 0, 0 },
},
0xff
},
{
{
{ 1, 1, 0, 0,
0, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 0, 1, 0, 0,
1, 1, 0, 0,
1, 0, 0, 0,
0, 0, 0, 0 },
{ 1, 1, 0, 0,
0, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0 },
{ 0, 1, 0, 0,
1, 1, 0, 0,
1, 0, 0, 0,
0, 0, 0, 0 },
},
0xff
}
};
static void
b_tetris_blend_down (BModule *module)
{
gint x, y, v;
guchar *pixel;
for (y = 0; y < module->height; y++)
for (v = 255; v >= 0; v -= 0xf)
{
for (x = 0; x < module->width; x++)
{
pixel = module->buffer + (y * module->width + x);
if (*pixel > v)
*pixel = v;
}
b_module_paint (module);
usleep (5000);
}
}
static gint
b_tetris_new_tile (void)
{
return random () % (sizeof (tile) / sizeof (tile[0]));
}
static gint
b_tetris_tile_width (gint tile_n,
gint rotation)
{
gint x, y, w = 0;
for (x = 0; x < 4; x++)
for (y = 0; y < 4; y++)
if (tile[tile_n].data[rotation][y*4+x])
w = x + 1;
return w;
}
static gint
b_tetris_tile_height (gint tile_n,
gint rotation)
{
gint x, y, h = 0;
for (y = 0; y < 4; y++)
for (x = 0; x < 4; x++)
if (tile[tile_n].data[rotation][y*4+x])
h = y + 1;
return h;
}
static gint
b_tetris_tile_left_offset (gint tile_n,
gint rotation)
{
gint o = 4, x, y;
for (y = 0; y < 4; y++)
for (x = 3; x >= 0; x--)
if (tile[tile_n].data[rotation][y*4+x])
if (o > x)
o = x;
return o;
}
static void
b_tetris_paint (BModule *module)
{
BTetris *tetris;
gchar *tile_data;
gint x, y;
gint tile_width, tile_height;
tetris = B_TETRIS (module);
memcpy (module->buffer, tetris->field, tetris->field_size);
tile_data = tile[tetris->tile].data[tetris->rotation];
tile_width = b_tetris_tile_width (tetris->tile, tetris->rotation);
tile_height = b_tetris_tile_height (tetris->tile, tetris->rotation);
#ifdef TETRIS_DEBUG
g_print ("b_tetris_paint: %d x %d\n", tile_width, tile_height);
#endif
if (tetris->n_completed == 0)
{
for (x = 0; x < tile_width; x++)
for (y = 0; y < tile_height; y++)
{
if (tile_data[y * 4 + x])
{
guchar *data = (module->buffer +
module->width * (tetris->y + y) +
(tetris->x + x) * 2);
data[0] = tile[tetris->tile].color;
data[1] = tile[tetris->tile].color;
}
}
}
b_module_paint (module);
}
static void
b_tetris_rotate (BModule *module,
gboolean clockwise)
{
BTetris *tetris = B_TETRIS (module);
guchar *tile_data;
gint x, y;
gint tile_width, tile_height, tile_leftoff;
gint rotation;
rotation = tetris->rotation;
if (clockwise)
rotation += 2;
rotation++;
rotation %= 4;
tile_data = tile[tetris->tile].data[rotation];
tile_width = b_tetris_tile_width (tetris->tile, rotation);
tile_height = b_tetris_tile_height (tetris->tile, rotation);
tile_leftoff = b_tetris_tile_left_offset (tetris->tile, rotation);
if (tetris->y + tile_height > module->height - BOTTOM_OFFSET)
return;
/* check if rotated tile collides with s.th. */
for (y = 0; y < tile_height; y++)
{
for (x = 0; x < tile_width; x++)
if (tile_data[y * 4 + x] &&
tetris->field[module->width *
(tetris->y + tile_height) + (tetris->x + x) * 2])
return;
}
while (tetris->x + tile_width > module->width/2)
tetris->x--;
while (tetris->x - tile_leftoff + 1 < 0)
tetris->x++;
tetris->rotation = rotation;
b_tetris_paint (module);
}
static gboolean
b_tetris_down (BModule *module)
{
BTetris *tetris = B_TETRIS (module);
guchar *tile_data;
gboolean stuck = FALSE;
gint x, y;
gint tile_width, tile_height;
tile_data = tile[tetris->tile].data[tetris->rotation];
tile_width = b_tetris_tile_width (tetris->tile, tetris->rotation);
tile_height = b_tetris_tile_height (tetris->tile, tetris->rotation);
if (tetris->y >= module->height - tile_height - BOTTOM_OFFSET)
{
stuck = TRUE;
}
else
{
for (x = 0; x < tile_width; x++)
for (y = 0; y < tile_height; y++)
if (tile_data[y * 4 + x] &&
tetris->field[module->width * (tetris->y + y + 1) +
(tetris->x + x) * 2] != 0)
{
stuck = TRUE;
break;
}
}
if (stuck)
{
if (tetris->y < 3)
tetris->game_over = TRUE;
if (tetris->speed > 100)
tetris->speed -= ACC_ON_STUCK;
for (x = 0; x < tile_width; x++)
for (y = 0; y < tile_height; y++)
{
if (tile_data[y * 4 + x])
{
guchar *data;
data = (tetris->field +
module->width * (tetris->y + y) +
2 * (tetris->x + x));
data[0] = tile[tetris->tile].color;
data[1] = tile[tetris->tile].color;
}
}
/* check for completed rows */
for (y = module->height-BOTTOM_OFFSET-1; y > 0; y--)
{
tetris->completed[y] = TRUE;
for (x = 0; x < module->width; x++)
{
if (tetris->field[module->width * y + x] == 0)
tetris->completed[y] = FALSE;
}
if (tetris->completed[y])
tetris->n_completed++;
}
if (tetris->n_completed)
tetris->n_completed = 5;
/* select new tile and start over */
tetris->y = 0;
tetris->x = module->width / 4 - 1;
tetris->rotation = 0;
tetris->tile = b_tetris_new_tile ();
}
else
{
tetris->y++;
}
b_tetris_paint (module);
return stuck;
}
static void
b_tetris_left (BModule *module)
{
BTetris *tetris = B_TETRIS (module);
gint x, y;
gint tile_width, tile_height;
guchar *tile_data;
tile_data = tile[tetris->tile].data[tetris->rotation];
tile_width = b_tetris_tile_width (tetris->tile, tetris->rotation);
tile_height = b_tetris_tile_height (tetris->tile, tetris->rotation);
if (tetris->x +
b_tetris_tile_left_offset (tetris->tile, tetris->rotation) <= 0)
return;
for (x = 0; x < tile_width+1; x++)
for (y = 0; y < tile_height; y++)
if (tile_data[y * 4 + x] &&
tetris->field[module->width * (tetris->y + y) +
(tetris->x + x) * 2 - 1] != 0)
return;
tetris->x--;
b_tetris_paint (module);
}
static void
b_tetris_right (BModule *module)
{
BTetris *tetris = B_TETRIS (module);
gint x, y;
gint tile_width, tile_height;
guchar *tile_data;
tile_data = tile[tetris->tile].data[tetris->rotation];
tile_width = b_tetris_tile_width (tetris->tile, tetris->rotation);
tile_height = b_tetris_tile_height (tetris->tile, tetris->rotation);
if (tetris->x * 2 >= (module->width -
b_tetris_tile_width (tetris->tile,
tetris->rotation) * 2 - 1))
return;
for (x = 0; x < tile_width+1; x++)
for (y = 0; y < tile_height; y++)
if (tile_data[y * 4 + x] &&
tetris->field[module->width * (tetris->y + y) +
(tetris->x + x + 1) * 2] != 0)
return;
tetris->x++;
b_tetris_paint (module);
}
G_MODULE_EXPORT gboolean
b_module_register (GTypeModule *module)
{
b_tetris_get_type (module);
return TRUE;
}
GType
b_tetris_get_type (GTypeModule *module)
{
if (!b_type_tetris)
{
static const GTypeInfo tetris_info =
{
sizeof (BTetrisClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) b_tetris_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (BTetris),
0, /* n_preallocs */
(GInstanceInitFunc) b_tetris_init,
};
b_type_tetris = g_type_module_register_type (module,
B_TYPE_MODULE, "BTetris",
&tetris_info, 0);
}
return b_type_tetris;
}
static void
b_tetris_class_init (BTetrisClass *klass)
{
BModuleClass *module_class;
module_class = B_MODULE_CLASS (klass);
module_class->max_players = 1;
module_class->query = b_tetris_query;
module_class->prepare = b_tetris_prepare;
module_class->relax = b_tetris_relax;
module_class->start = b_tetris_start;
module_class->stop = b_tetris_stop;
module_class->event = b_tetris_event;
module_class->tick = b_tetris_tick;
module_class->describe = b_tetris_describe;
}
static void
b_tetris_init (BTetris *tetris)
{
tetris->x = 0;
tetris->y = 0;
tetris->rotation = 0;
tetris->speed = START_SPEED;
tetris->tile = -1;
tetris->completed = NULL;
tetris->n_completed = 0;
tetris->player_device_id = -1;
}
static gboolean
b_tetris_query (gint width,
gint height,
gint channels,
gint maxval)
{
#ifdef TETRIS_DEBUG
g_print ("b_tetris_query\n");
#endif
return (width > 8 && height > 6 && channels == 1 && maxval == 255);
}
static gboolean
b_tetris_prepare (BModule *module,
GError **error)
{
BTetris *tetris = B_TETRIS (module);
#ifdef TETRIS_DEBUG
g_print ("b_tetris_prepare\n");
#endif
tetris->field_size = module->width * module->height;
tetris->field = g_new0 (guchar, tetris->field_size);
tetris->completed = g_new0 (gboolean, module->width);
return TRUE;
}
static void
b_tetris_relax (BModule *module)
{
BTetris *tetris = B_TETRIS (module);
#ifdef TETRIS_DEBUG
g_print ("b_tetris_relax\n");
#endif
if (tetris->field)
{
g_free (tetris->field);
tetris->field = NULL;
}
if (tetris->completed)
{
g_free (tetris->completed);
tetris->completed = NULL;
}
}
static void
b_tetris_start (BModule *module)
{
BTetris *tetris;
#ifdef TETRIS_DEBUG
g_print ("b_tetris_start\n");
#endif
tetris = B_TETRIS (module);
tetris->x = module->width / 4 - 1;
tetris->y = 0;
tetris->rotation = 0;
tetris->tile = b_tetris_new_tile ();
tetris->game_over = FALSE;
tetris->speed = START_SPEED;
#ifdef TETRIS_DEBUG
g_print (" tetris tile: %d\n", tetris->tile);
#endif
memset (tetris->field, 0, tetris->field_size);
memset (tetris->completed, 0, module->width);
tetris->flash_state = FALSE;
tetris->n_completed = 0;
tetris->falling = FALSE;
b_module_fill (module, 0);
b_module_ticker_start (module, 1000);
}
static void
b_tetris_stop (BModule *module)
{
BTetris *tetris;
#ifdef TETRIS_DEBUG
g_print ("b_tetris_stop\n");
#endif
tetris = B_TETRIS (module);
b_module_fill (module, 0);
b_module_paint (module);
}
static void
b_tetris_event (BModule *module,
BModuleEvent *event)
{
BTetris *tetris;
#ifdef TETRIS_DEBUG
g_print ("b_tetris_event\n");
#endif
tetris = B_TETRIS (module);
if (tetris->falling)
return;
switch (event->type)
{
case B_EVENT_TYPE_KEY:
if (event->device_id == tetris->player_device_id)
{
switch (event->key)
{
case B_KEY_4:
b_tetris_left (module);
break;
case B_KEY_5:
b_tetris_rotate (module, TRUE);
break;
case B_KEY_6:
b_tetris_right (module);
break;
case B_KEY_7:
b_tetris_rotate (module, TRUE);
break;
case B_KEY_8:
if (!b_tetris_down (module))
tetris->falling = TRUE;
break;
case B_KEY_9:
b_tetris_rotate (module, FALSE);
break;
default:
break;
}
}
break;
case B_EVENT_TYPE_PLAYER_ENTERED:
if (tetris->player_device_id == -1)
{
#ifdef TETRIS_DEBUG
g_print ("BTetris: player %d entered.\n", event->device_id);
#endif
tetris->player_device_id = event->device_id;
module->num_players++;
}
break;
case B_EVENT_TYPE_PLAYER_LEFT:
if (tetris->player_device_id == event->device_id)
{
tetris->player_device_id = -1;
module->num_players--;
}
break;
default:
break;
}
}
static gint
b_tetris_tick (BModule *module)
{
BTetris *tetris;
gint y, delta = 0;
#ifdef TETRIS_DEBUG
g_print ("b_tetris_tick\n");
#endif
tetris = B_TETRIS (module);
if (tetris->game_over)
{
b_tetris_blend_down (module);
b_module_request_stop (module);
return 0;
}
if (tetris->falling)
{
if (b_tetris_down (module))
{
tetris->falling = FALSE;
}
else
return FALL_SPEED;
}
if (tetris->n_completed > 0)
{
/* flash completed rows */
if (tetris->flash_state)
{
memcpy (module->buffer, tetris->field, tetris->field_size);
b_module_paint (module);
}
else
{
for (y = 0; y < module->height; y++)
if (tetris->completed[y])
memset (module->buffer + module->width * y, 0, module->width);
b_module_paint (module);
/* finally remove completed rows */
if (--tetris->n_completed == 0)
{
for (y = module->height - BOTTOM_OFFSET - 1; y >= 0; y--)
if (tetris->completed[y - delta])
{
memmove (tetris->field + module->width,
tetris->field,
module->width * (y));
delta++;
y++;
}
memcpy (module->buffer, tetris->field, tetris->field_size);
b_module_paint (module);
memset (tetris->completed, 0, sizeof(gboolean) * module->height);
return tetris->speed;
}
}
tetris->flash_state = !(tetris->flash_state);
return FLASH_SPEED;
}
if (b_tetris_down (module)) /* if stuck */
return b_tetris_tick (module);
return tetris->speed;
}
static void
b_tetris_describe (BModule *module,
const gchar **title,
const gchar **description,
const gchar **author)
{
*title = "BTetris";
*description = "Tetris game";
*author = "Daniel Mack";
}