syndilights/blib-1.1.7/modules/bretris.c

702 lines
21 KiB
C

/* BRetris: Tetris (from right to left) module for Blinkenlights
*
* Copyright (c) 2003 1stein <1stein@schuermans.info>
*
* based on Test implementation for a BModule by the Blinkenlights Crew
*
* 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 "config.h"
#include <string.h>
#include <stdlib.h>
#include <glib-object.h>
#include <gmodule.h>
#include <blib/blib.h>
#define BRetrisVerMaj 1
#define BRetrisVerMin 0
#define BRetrisVerRev 2
//minimum size and color count of display
#define BRetrisSizeXMin 18
#define BRetrisSizeYMin 8
#define BRetrisMaxColorMin 1
//speed of game (small number = fast)
#define BRetrisSpeedMin 500
#define BRetrisSpeedMax 50
#define BRetrisSpeedInc 7 //how fast to increase speed
#define BRetrisSpeedDrop 20 //speed for dropping stones
//the colors
#define BRetrisColorEmpty( MaxColor ) (0)
#define BRetrisColorActiveStone( MaxColor ) (MaxColor)
#define BRetrisColorStone( MaxColor ) ((MaxColor) * 3 / 4)
#define BRetrisColorRemoveStone( MaxColor ) (MaxColor)
#define count( array ) (sizeof( (array) ) / sizeof( (array)[0] ))
#ifdef RETRIS_DEBUG
#define dbg_print g_print
#else
#define dbg_print(fmt, ...)
#endif
#define B_TYPE_RETRIS_MODULE (b_type_retris_module)
#define B_RETRIS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), B_TYPE_RETRIS_MODULE, BRetrisModule))
#define B_RETRIS_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), B_TYPE_RETRIS_MODULE, BRetrisModuleClass))
#define B_IS_RETRIS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), B_TYPE_RETRIS_MODULE))
typedef struct _BRetrisModule BRetrisModule;
typedef struct _BRetrisModuleClass BRetrisModuleClass;
struct _BRetrisModule
{
BModule parent_instance;
int SizeX, SizeY; //size of game-field
int MaxColor; //maximum color value
int PlayerId; //device id of the player
int Speed; //current speed
int * * ppStones; //two-dimensional array with stones [SizeX+4][SizeY]
int CurStone; //number of current stone, -1 if none
int CurX, CurY; //position of current stone
int CurRot; //rotation of current stone
int RemoveCol; //column being removed, -1 if none
int Dropping; //flag if dropping current stone
};
struct _BRetrisModuleClass
{
BModuleClass parent_class;
};
static GType b_retris_module_get_type (GTypeModule *module);
static void b_retris_module_class_init (BRetrisModuleClass *klass);
static void b_retris_module_init (BRetrisModule *test_module);
static gboolean b_retris_module_query (gint width,
gint height,
gint channels,
gint maxval);
static gboolean b_retris_module_prepare (BModule *module,
GError **error);
static void b_retris_module_relax (BModule *module);
static void b_retris_module_start (BModule *module);
static void b_retris_module_event (BModule *module,
BModuleEvent *event);
static gint b_retris_module_tick (BModule *module);
static void b_retris_module_describe (BModule *module,
const gchar **title,
const gchar **description,
const gchar **author);
static GType b_type_retris_module = 0;
G_MODULE_EXPORT gboolean
b_module_register (GTypeModule * module)
{
b_retris_module_get_type (module);
return TRUE;
}
GType
b_retris_module_get_type (GTypeModule * module)
{
if (!b_type_retris_module)
{
static const GTypeInfo retris_module_info = {
sizeof (BRetrisModuleClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) b_retris_module_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (BRetrisModule),
0, /* n_preallocs */
(GInstanceInitFunc) b_retris_module_init,
};
/* !!!!!!!!! The name given in the next function MUST be unique! */
b_type_retris_module = g_type_module_register_type (module,
B_TYPE_MODULE,
"BRetris",
&retris_module_info,
0);
}
return b_type_retris_module;
}
//stones (always 4 lines with same stone in different rotation angles)
typedef struct sBRetrisStonePos
{
int X, Y;
} stBRetrisStonePos;
typedef struct sBRetrisStoneRot
{
stBRetrisStonePos Pos[4];
} stBRetrisStoneRot;
typedef struct sBRetrisStone
{
stBRetrisStoneRot Rot[4];
} stBRetrisStone;
stBRetrisStone BRetrisStoneTable[] =
{
{ { { { { -2, 0 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } }, //the I
{ { { 0, -2 }, { 0, -1 }, { 0, 0 }, { 0, 1 } } },
{ { { -2, 0 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } },
{ { { 0, -2 }, { 0, -1 }, { 0, 0 }, { 0, 1 } } } } },
{ { { { { 1, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } }, //the L
{ { { 0, -1 }, { 0, 0 }, { 0, 1 }, { 1, 1 } } },
{ { { -1, 0 }, { 0, 0 }, { 1, 0 }, { -1, 1 } } },
{ { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } } } } },
{ { { { { -1, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } }, //the J
{ { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 0, 1 } } },
{ { { -1, 0 }, { 0, 0 }, { 1, 0 }, { 1, 1 } } },
{ { { 0, -1 }, { 0, 0 }, { -1, 1 }, { 0, 1 } } } } },
{ { { { { 0, -1 }, { -1, 0 }, { 0, 0 }, { 0, 1 } } }, //the T
{ { { 0, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } },
{ { { 0, -1 }, { 0, 0 }, { 1, 0 }, { 0, 1 } } },
{ { { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, 1 } } } } },
{ { { { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 1, 0 } } }, //the block
{ { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 1, 0 } } },
{ { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 1, 0 } } },
{ { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 1, 0 } } } } },
{ { { { { 0, -1 }, { -1, 0 }, { 0, 0 }, { -1, 1 } } }, //the Z
{ { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 1, 0 } } },
{ { { 0, -1 }, { -1, 0 }, { 0, 0 }, { -1, 1 } } },
{ { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 1, 0 } } } } },
{ { { { { -1, -1 }, { -1, 0 }, { 0, 0 }, { 0, 1 } } }, //the S
{ { { 0, -1 }, { 1, -1 }, { -1, 0 }, { 0, 0 } } },
{ { { -1, -1 }, { -1, 0 }, { 0, 0 }, { 0, 1 } } },
{ { { 0, -1 }, { 1, -1 }, { -1, 0 }, { 0, 0 } } } } },
};
//check if a stone fits at a position
//retuns true if stone fits
static int
BRetrisCheckStone (BRetrisModule *pRetrisModule,
int Stone, int PosX, int PosY, int Rot)
{
stBRetrisStoneRot * pStoneRot;
stBRetrisStonePos * pStonePos;
int I, X, Y, SizeX, SizeY;
//check if stone is available
if (Stone < 0 || Stone >= count (BRetrisStoneTable))
return 0;
//get size of stone array
SizeX = pRetrisModule->SizeX + 4;
SizeY = pRetrisModule->SizeY;
//get pointer to stone rotation
pStoneRot = &BRetrisStoneTable[Stone]
.Rot[Rot & 3];
//all 4 positions
for (I = 0; I < 4; I++)
{
//get position
pStonePos = &pStoneRot->Pos[I];
//get coordinates
X = PosX + pStonePos->X;
Y = PosY + pStonePos->Y;
//outside of array
if( X < 0 || X >= SizeX || Y < 0 || Y >= SizeY )
return 0;
//position not free
if( pRetrisModule->ppStones[X][Y] )
return 0;
}
//stone fits
return 1;
}
//deactivate stone
static void
BRetrisDeactivateStone (BRetrisModule *pRetrisModule)
{
stBRetrisStoneRot * pStoneRot;
stBRetrisStonePos * pStonePos;
int I, X, Y, SizeX, SizeY;
//check if stone is available
if (pRetrisModule->CurStone < 0 || pRetrisModule->CurStone >= count (BRetrisStoneTable))
return;
//get size of stone array
SizeX = pRetrisModule->SizeX + 4;
SizeY = pRetrisModule->SizeY;
//get pointer to stone rotation
pStoneRot = &BRetrisStoneTable[pRetrisModule->CurStone]
.Rot[pRetrisModule->CurRot & 3];
//all 4 positions
for (I = 0; I < 4; I++)
{
//get position
pStonePos = &pStoneRot->Pos[I];
//get coordinates
X = pRetrisModule->CurX + pStonePos->X;
Y = pRetrisModule->CurY + pStonePos->Y;
//put stone into stone array
if( X >= 0 && X < SizeX && Y >= 0 && Y < SizeY )
pRetrisModule->ppStones[X][Y] = 1;
}
}
//draw active stone
static void
BRetrisDrawStone (BRetrisModule *pRetrisModule)
{
stBRetrisStoneRot * pStoneRot;
stBRetrisStonePos * pStonePos;
int I, X, Y, SizeX, SizeY;
//check if stone is available
if (pRetrisModule->CurStone < 0 || pRetrisModule->CurStone >= count (BRetrisStoneTable))
return;
//get size of display
SizeX = pRetrisModule->SizeX;
SizeY = pRetrisModule->SizeY;
//get pointer to stone rotation
pStoneRot = &BRetrisStoneTable[pRetrisModule->CurStone]
.Rot[pRetrisModule->CurRot & 3];
//all 4 positions
for (I = 0; I < 4; I++)
{
//get position
pStonePos = &pStoneRot->Pos[I];
//get coordinates
X = pRetrisModule->CurX + pStonePos->X;
Y = pRetrisModule->CurY + pStonePos->Y;
//draw position
if( X >= 0 && X < SizeX && Y >= 0 && Y < SizeY )
b_module_draw_point ((BModule *) pRetrisModule,
X, Y,
BRetrisColorActiveStone (pRetrisModule->MaxColor));
}
}
//output current picture
static void
BRetrisOutput (BRetrisModule *pRetrisModule)
{
int X, Y;
//empty the screen
b_module_fill ((BModule *) pRetrisModule,
BRetrisColorEmpty (pRetrisModule->MaxColor));
//draw stones
for (X = 0; X < pRetrisModule->SizeX; X++)
{
for (Y = 0; Y < pRetrisModule->SizeY; Y++)
{
if (pRetrisModule->ppStones[X][Y])
{
if (X == pRetrisModule->RemoveCol)
b_module_draw_point ((BModule *) pRetrisModule,
X, Y,
BRetrisColorRemoveStone (pRetrisModule->MaxColor));
else
b_module_draw_point ((BModule *) pRetrisModule,
X, Y,
BRetrisColorStone (pRetrisModule->MaxColor));
}
}
}
//draw active stone
BRetrisDrawStone (pRetrisModule);
//update screen
b_module_paint ((BModule *) pRetrisModule);
}
//start a new game
static void
BRetrisNewGame (BRetrisModule *pRetrisModule)
{
int X, Y;
//set speed to minimal
pRetrisModule->Speed = BRetrisSpeedMin;
//remove all stones
for (X = 0; X < pRetrisModule->SizeX + 4; X++)
for (Y = 0; Y < pRetrisModule->SizeY; Y++)
pRetrisModule->ppStones[X][Y] = 0;
//no current stone yet
pRetrisModule->CurStone = -1;
//no column ist being removed
pRetrisModule->RemoveCol = -1;
//no stone is being dropped
pRetrisModule->Dropping = 0;
dbg_print ("BRetris: new game\n");
//output current picture
BRetrisOutput (pRetrisModule);
}
//key-procedure
static void
BRetrisKey (BRetrisModule *pRetrisModule,
BModuleKey Key)
{
switch (Key)
{
//move stone up
case B_KEY_2:
if (BRetrisCheckStone (pRetrisModule,
pRetrisModule->CurStone,
pRetrisModule->CurX,
pRetrisModule->CurY - 1,
pRetrisModule->CurRot))
{
pRetrisModule->CurY--;
BRetrisOutput (pRetrisModule);
}
break;
//rotate stone
case B_KEY_3:
case B_KEY_6:
if (BRetrisCheckStone (pRetrisModule,
pRetrisModule->CurStone,
pRetrisModule->CurX,
pRetrisModule->CurY,
(pRetrisModule->CurRot + 1) & 3))
{
pRetrisModule->CurRot = (pRetrisModule->CurRot + 1) & 3;
BRetrisOutput (pRetrisModule);
}
break;
//drop stone
case B_KEY_4:
pRetrisModule->Dropping = 1; //set flag for dropping stone
b_module_ticker_stop ((BModule *) pRetrisModule); //restart the tick machinery
b_module_ticker_start ((BModule *) pRetrisModule, BRetrisSpeedDrop);
break;
//move stone down
case B_KEY_8:
if (BRetrisCheckStone (pRetrisModule,
pRetrisModule->CurStone,
pRetrisModule->CurX,
pRetrisModule->CurY + 1,
pRetrisModule->CurRot))
{
pRetrisModule->CurY++;
BRetrisOutput (pRetrisModule);
}
break;
//rotate stone in other direction
case B_KEY_9:
if (BRetrisCheckStone (pRetrisModule,
pRetrisModule->CurStone,
pRetrisModule->CurX,
pRetrisModule->CurY,
(pRetrisModule->CurRot + 3) & 3))
{
pRetrisModule->CurRot = (pRetrisModule->CurRot + 3) & 3;
BRetrisOutput (pRetrisModule);
}
break;
default:
break;
}
}
//tick-procedure
//returns 1 if game over or 0 if game not over
static int
BRetrisTick (BRetrisModule *pRetrisModule)
{
int X, Y;
//dropping a stone
if (pRetrisModule->Dropping)
{
//move stone one down
if (BRetrisCheckStone (pRetrisModule,
pRetrisModule->CurStone,
pRetrisModule->CurX - 1,
pRetrisModule->CurY,
pRetrisModule->CurRot))
pRetrisModule->CurX--;
//stone has hit ground
else
//stop dropping stone
pRetrisModule->Dropping = 0;
} //dropping a stone
//not dropping a stone
else
{
//stone active
if (pRetrisModule->CurStone >= 0)
{
//move stone one down
if (BRetrisCheckStone (pRetrisModule,
pRetrisModule->CurStone,
pRetrisModule->CurX - 1,
pRetrisModule->CurY,
pRetrisModule->CurRot))
pRetrisModule->CurX--;
//stone has hit ground
else
{
BRetrisDeactivateStone (pRetrisModule); //deactivate stone
pRetrisModule->CurStone = -1; //no current stone
dbg_print( "BRetris: stone hit ground\n" );
}
} //stone active
//no active stone
else
{
//column is being removed
if (pRetrisModule->RemoveCol >= 0)
{
//remove the column
for (X = pRetrisModule->RemoveCol; X < pRetrisModule->SizeX + 4 - 1; X++)
for (Y = 0; Y < pRetrisModule->SizeY; Y++)
pRetrisModule->ppStones[X][Y] = pRetrisModule->ppStones[X + 1][Y];
dbg_print( "BRetris: column %d removed\n", pRetrisModule->RemoveCol );
pRetrisModule->RemoveCol = -1; //no column is being removed
//increase speed
pRetrisModule->Speed = (pRetrisModule->Speed * (BRetrisSpeedInc - 1) + BRetrisSpeedMax) / BRetrisSpeedInc;
} //column is being removed
//no column is being removed
else
{
//check for a column to remove
for (X = 0; X < pRetrisModule->SizeX + 4; X++)
{
for (Y = 0; Y < pRetrisModule->SizeY; Y++)
if (! pRetrisModule->ppStones[X][Y])
break;
if (Y >= pRetrisModule->SizeY) //column is full
break;
}
//found a column to remove
if (X < pRetrisModule->SizeX + 4)
{
pRetrisModule->RemoveCol = X;
dbg_print( "BRetris: column %d is full\n", pRetrisModule->RemoveCol );
}
//found no column to remove
else
{
//check game over
for (X = pRetrisModule->SizeX; X < pRetrisModule->SizeX + 4; X++)
for (Y = 0; Y < pRetrisModule->SizeY; Y++)
if (pRetrisModule->ppStones[X][Y])
//found inactive stone right of display - game over
return 1;
//create new stone
pRetrisModule->CurStone = rand( ) % count (BRetrisStoneTable);
pRetrisModule->CurX = pRetrisModule->SizeX + 2;
pRetrisModule->CurY = pRetrisModule->SizeY / 2;
pRetrisModule->CurRot = rand( ) & 3;
dbg_print( "BRetris: created new stone\n" );
} //found no column to remove
} //no column is being removed
} //no active stone
} //not dropping a stone
//output new picture
BRetrisOutput (pRetrisModule);
//return game not over
return 0;
}
static void
b_retris_module_class_init (BRetrisModuleClass *klass)
{
BModuleClass *module_class;
module_class = B_MODULE_CLASS (klass);
module_class->query = b_retris_module_query;
module_class->prepare = b_retris_module_prepare;
module_class->relax = b_retris_module_relax;
module_class->start = b_retris_module_start;
module_class->event = b_retris_module_event;
module_class->tick = b_retris_module_tick;
module_class->describe = b_retris_module_describe;
}
static void
b_retris_module_init (BRetrisModule *retris_module)
{
}
static gboolean
b_retris_module_query (gint width,
gint height,
gint channels,
gint maxval)
{
return (width >= BRetrisSizeXMin && height >= BRetrisSizeYMin
&& channels == 1 && maxval >= BRetrisMaxColorMin);
}
static gboolean
b_retris_module_prepare (BModule *module,
GError **error)
{
int SizePtrs, SizeCol, Size, X;
char * Ptr;
BRetrisModule *pRetris_module = B_RETRIS_MODULE (module);
//initialize the module values that depend on the output device
pRetris_module->SizeX = module->width; //size of game field
pRetris_module->SizeY = module->height;
pRetris_module->MaxColor = module->maxval; //maximum color value
pRetris_module->PlayerId = -1; //no plyer in game yet
//allocate needed memory
SizePtrs = (pRetris_module->SizeX + 4) * sizeof( int * ); //get needed size
SizeCol = pRetris_module->SizeY * sizeof( int );
Size = SizePtrs + (pRetris_module->SizeX + 4) * SizeCol;
Ptr = g_new (gchar, Size);
pRetris_module->ppStones = (int * *)Ptr; //remember pointer
for( X = 0, Ptr += SizePtrs; X < pRetris_module->SizeX + 4; X++, Ptr += SizeCol ) //generate pointers to columns
pRetris_module->ppStones[X] = (int *)Ptr;
return TRUE;
}
static void
b_retris_module_relax (BModule *module)
{
BRetrisModule *pRetris_module = B_RETRIS_MODULE (module);
g_free (pRetris_module->ppStones);
pRetris_module->ppStones = NULL;
}
static void
b_retris_module_start (BModule *module)
{
//start new game
BRetrisNewGame ((BRetrisModule *) module);
//start the tick machinery
b_module_ticker_start (module, BRetrisSpeedMin);
}
static void
b_retris_module_event (BModule *module,
BModuleEvent *event)
{
BRetrisModule *retris = B_RETRIS_MODULE (module);
switch (event->type)
{
case B_EVENT_TYPE_KEY:
BRetrisKey (retris, event->key);
break;
case B_EVENT_TYPE_PLAYER_ENTERED:
if (retris->PlayerId == -1)
{
retris->PlayerId = event->device_id;
module->num_players++;
}
break;
case B_EVENT_TYPE_PLAYER_LEFT:
if (retris->PlayerId == event->device_id)
{
retris->PlayerId = -1;
module->num_players--;
}
break;
default:
break;
}
}
static gint
b_retris_module_tick (BModule *module)
{
int GameOver;
//call tick-procedure
GameOver = BRetrisTick ((BRetrisModule *) module);
//game over
if( GameOver )
{
dbg_print ("BRetris: requesting stop\n");
//clear screen
b_module_fill (module, 0);
b_module_paint (module);
//request end of game
b_module_request_stop (module);
//we do not want to be called again
return 0;
}
//dropping a stone at the moment
if (((BRetrisModule *) module)->Dropping)
//we want to be called again in very few milliseconds
return BRetrisSpeedDrop;
//we want to be called again in some milliseconds
return ((BRetrisModule *) module)->Speed;
}
static void
b_retris_module_describe (BModule *module,
const gchar **title,
const gchar **description,
const gchar **author)
{
*title = "BRetris";
*description = "Tetris game from right to left";
*author = "1stein";
}