Well you are right about the array. So in order to make a 'piece' fit into the array paradigm you need a function that can take a piece 'definition' so to speak which would inform the game about which array elements to 'turn on' to show the piece. If you do it right, by using offsets you should be able to get away with simply setting the start row and column of the piece in question and have a piece type. The piece type then would index into a piece defintion array that tells how to draw that piece. If you do it this way, moving the piece left/right and down/up becomes a snap because you simply increment/decrement start row and column since everything being drawn on screen from memory is relative to the starting offset of the piece.
After you get the drawing routine down eliminating rows is a simple matter of eliminating the bottom row in memory and then doing a reverse bubble sort to move the other rows down.
The best place to track row deletions is when you draw. If the total number of same colored squares equals the total number of columns on the board, you have a deletion row. Add this row to a vector or array. At the end of the drawing function you then iterate the array or vector and call DeleteRow(DeletionArray[iIndex]) for each row in the deletion array. Then reverse bubble sort the array and return. Since you will be re-drawing the board on the next game iteration, you don't need to draw twice. The new information will be reflected to the player when the screen is refreshed.
For special effects when deleting rows you would have to implement something a bit different but it would not be too difficult.
Code:
enum BLOCK_ROT
{
DEG0,
DEG90,
DEG180,
DEG270
};
struct BlockDef
{
int iRowOffset;
int iColOffset;
}
class CGamePiece
{
BlockDef *m_pPieceDef;
int m_iNumBlocks;
BLOCK_ROT m_iRotationType;
...
};
class CTetrisPiece
{
CGamePiece *m_pPieces;
int m_iTotalPieces;
int m_iMaxPieces;
DWORD m_dwRow;
DWORD m_dwCol;
DWORD m_dwOffset;
BLOCK_ROT m_iCurRotation;
...
...
public:
void Create(int iMaxPieces.....) {m_pPieces=new CGamePiece[iMaxPieces]; m_iMaxPieces=iMaxPieces; ..... }
bool AddPiece(CGamePiece *pPiece)
{
if (m_iTotalPieces<m_iMaxPieces)
{
//Add piece here
m_iTotalPieces++;
return false;
} else return true;
}
bool AddPiece(BlockDef *pBlocks,int iNumBlocks);
....
};
class CArray2D
{
protected:
DWORD *m_pArray;
DWORD m_dwWidth;
DWORD m_dwHeight;
DWORD m_dwMaxOffset;
public:
CArray2D():m_pArray(NULL),m_dwWidth(0),m_dwHeight(0),m_dwMaxOffset(0) { }
bool Create(DWORD dwWidth,DWORD dwHeight)
{
m_dwWidth=dwWidth;
m_dwHeight=dwHeight;
m_dwMaxOffset=m_dwWidth*m_dwHeight;
m_pArray=new DWORD[m_dwMaxOffset];
if (!m_pArray)
{
return false;
} else return true;
}
DWORD GetValue(bool &bFailed);
DWORD GetValue(DWORD dwRow,DWORD dwCol,bool &bFailed);
bool SetValue(DWORD dwOffset);
bool SetValue(DWORD dwRow,DWORD dwCol);
};
class CTetrisBoard
{
protected:
CArray *m_pBoard;
void SortRows();
public:
....
void Update(float fFrameDelta);
void Render();
bool DeleteRow(DWORD dwRow);
};
That's a good start. I left a lot out and you might also want to employ the STL more to ease your pain. There are array wrappers already available to you so writing your own isn't completely necessary.