726 lines
19 KiB
C
726 lines
19 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include "../../autoconf.h"
|
|
#include "../../pixel.h"
|
|
#include "../../util.h"
|
|
#include "../../scrolltext/scrolltext.h"
|
|
#include "variants.h"
|
|
#include "piece.h"
|
|
#include "bucket.h"
|
|
#include "view.h"
|
|
|
|
#define WAIT(ms) wait(ms)
|
|
|
|
/**
|
|
* \defgroup TetrisViewDefinesPrivate View: Internal constants
|
|
*/
|
|
/*@{*/
|
|
|
|
/***********
|
|
* defines *
|
|
***********/
|
|
|
|
/** how often should the border blink (to indicate level up) */
|
|
#define TETRIS_VIEW_BORDER_BLINK_COUNT 2
|
|
/** amount of time (in ms) between border color changes */
|
|
#define TETRIS_VIEW_BORDER_BLINK_DELAY 100
|
|
|
|
/** how often should the lines blink when they get removed */
|
|
#define TETRIS_VIEW_LINE_BLINK_COUNT 3
|
|
/** amount of time (in ms) between line color changes */
|
|
#define TETRIS_VIEW_LINE_BLINK_DELAY 75
|
|
|
|
/** color of space */
|
|
#define TETRIS_VIEW_COLORSPACE 0
|
|
/** color of border */
|
|
#define TETRIS_VIEW_COLORBORDER 1
|
|
/** color of fading lines */
|
|
#define TETRIS_VIEW_COLORFADE 2
|
|
/** color of a piece */
|
|
#define TETRIS_VIEW_COLORPIECE 3
|
|
/** color of pause mode */
|
|
#define TETRIS_VIEW_COLORPAUSE 1
|
|
/** color of line counter */
|
|
#define TETRIS_VIEW_COLORCOUNTER 2
|
|
|
|
|
|
#ifdef GAME_TETRIS_FP
|
|
#if NUM_ROWS < NUM_COLS
|
|
#define VIEWCOLS NUM_ROWS
|
|
#define VIEWROWS NUM_ROWS
|
|
#elif NUM_ROWS > NUM_COLS
|
|
#define VIEWCOLS NUM_COLS
|
|
#define VIEWROWS NUM_COLS
|
|
#else
|
|
#define VIEWCOLS NUM_COLS
|
|
#define VIEWROWS NUM_ROWS
|
|
#endif
|
|
#else
|
|
#define VIEWCOLS NUM_COLS
|
|
#define VIEWROWS NUM_ROWS
|
|
#endif
|
|
|
|
|
|
#if VIEWROWS >= 20
|
|
#define TETRIS_VIEW_YOFFSET_DUMP ((VIEWROWS - 20) / 2)
|
|
#define TETRIS_VIEW_HEIGHT_DUMP 20
|
|
#else
|
|
#define TETRIS_VIEW_YOFFSET_DUMP 0
|
|
#define TETRIS_VIEW_HEIGHT_DUMP VIEWROWS
|
|
#endif
|
|
|
|
|
|
#if VIEWCOLS >= 16
|
|
#define TETRIS_VIEW_XOFFSET_DUMP (((VIEWCOLS - 16) / 2) + 1)
|
|
#define TETRIS_VIEW_WIDTH_DUMP 10
|
|
|
|
#if VIEWROWS >= 16
|
|
#define TETRIS_VIEW_XOFFSET_COUNTER \
|
|
(TETRIS_VIEW_XOFFSET_DUMP + TETRIS_VIEW_WIDTH_DUMP + 1)
|
|
#define TETRIS_VIEW_YOFFSET_COUNT100 ((VIEWCOLS - 14) / 2)
|
|
#define TETRIS_VIEW_YOFFSET_COUNT10 (TETRIS_VIEW_YOFFSET_COUNT100 + 2)
|
|
#define TETRIS_VIEW_YOFFSET_COUNT1 (TETRIS_VIEW_YOFFSET_COUNT10 + 4)
|
|
|
|
#define TETRIS_VIEW_XOFFSET_PREVIEW \
|
|
(TETRIS_VIEW_XOFFSET_DUMP + TETRIS_VIEW_WIDTH_DUMP + 1)
|
|
#define TETRIS_VIEW_YOFFSET_PREVIEW (TETRIS_VIEW_YOFFSET_COUNT1 + 4)
|
|
#elif VIEWROWS < 16 && VIEWROWS >= 4
|
|
#define TETRIS_VIEW_XOFFSET_PREVIEW \
|
|
(TETRIS_VIEW_XOFFSET_DUMP + TETRIS_VIEW_WIDTH_DUMP + 1)
|
|
#define TETRIS_VIEW_YOFFSET_PREVIEW ((VIEWROWS - 4) / 2)
|
|
#endif
|
|
#elif (VIEWCOLS < 16) && (VIEWCOLS >= 12)
|
|
#define TETRIS_VIEW_XOFFSET_DUMP ((VIEWCOLS - 10) / 2)
|
|
#define TETRIS_VIEW_WIDTH_DUMP 10
|
|
#elif VIEWCOLS == 11
|
|
#define TETRIS_VIEW_XOFFSET_DUMP 1
|
|
#define TETRIS_VIEW_WIDTH_DUMP 10
|
|
#else
|
|
#define TETRIS_VIEW_XOFFSET_DUMP 0
|
|
#define TETRIS_VIEW_WIDTH_DUMP VIEWCOLS
|
|
#endif
|
|
|
|
|
|
/*@}*/
|
|
|
|
|
|
/**
|
|
* \defgroup TetrisViewNoInterface View: Internal non-interface functions
|
|
*/
|
|
/*@{*/
|
|
|
|
/***************************
|
|
* non-interface functions *
|
|
***************************/
|
|
|
|
/**
|
|
* setpixel replacement which may transform the pixel coordinates
|
|
* @param nBearing bearing of the view
|
|
* @param x x-coordinate of the pixel
|
|
* @param y y-coordinate of the pixel
|
|
* @param nColor Color of the pixel
|
|
*/
|
|
void tetris_view_setpixel(tetris_bearing_t nBearing,
|
|
uint8_t x,
|
|
uint8_t y,
|
|
uint8_t nColor)
|
|
{
|
|
x = VIEWCOLS - 1 - x;
|
|
|
|
switch (nBearing)
|
|
{
|
|
case TETRIS_BEARING_0:
|
|
setpixel((pixel){x, y}, nColor);
|
|
break;
|
|
case TETRIS_BEARING_90:
|
|
setpixel((pixel){y, VIEWCOLS - 1 - x}, nColor);
|
|
break;
|
|
case TETRIS_BEARING_180:
|
|
setpixel((pixel){VIEWCOLS - 1 - x, VIEWROWS - 1 - y}, nColor);
|
|
break;
|
|
case TETRIS_BEARING_270:
|
|
setpixel((pixel){VIEWROWS - 1 - y, x}, nColor);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* draws a horizontal line
|
|
* @param nBearing bearing of the view
|
|
* @param x1 first x-coordinate of the line
|
|
* @param x2 second x-coordinate of the line
|
|
* @param y y-coordinate of the line
|
|
* @param nColor Color of the line
|
|
*/
|
|
void tetris_view_drawHLine(tetris_bearing_t nBearing,
|
|
uint8_t x1,
|
|
uint8_t x2,
|
|
uint8_t y,
|
|
uint8_t nColor)
|
|
{
|
|
assert(x1 <= x2);
|
|
|
|
for (uint8_t x = x1; x <= x2; ++x)
|
|
{
|
|
tetris_view_setpixel(nBearing, x, y, nColor);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* draws a vertical line
|
|
* @param nBearing bearing of the view
|
|
* @param x x-coordinate of the line
|
|
* @param y1 first y-coordinate of the line
|
|
* @param y2 second y-coordinate of the line
|
|
* @param nColor Color of the line
|
|
*/
|
|
void tetris_view_drawVLine(tetris_bearing_t nBearing,
|
|
uint8_t x,
|
|
uint8_t y1,
|
|
uint8_t y2,
|
|
uint8_t nColor)
|
|
{
|
|
assert(y1 <= y2);
|
|
|
|
for (uint8_t y = y1; y <= y2; ++y)
|
|
{
|
|
tetris_view_setpixel(nBearing, x, y, nColor);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* helper function to dim the piece color if game is paused
|
|
* @param pV pointer to the view whose pause status is of interest
|
|
*/
|
|
uint8_t tetris_view_getPieceColor(tetris_view_t *pV)
|
|
{
|
|
if (pV->modeCurrent == TETRIS_VIMO_RUNNING)
|
|
{
|
|
return TETRIS_VIEW_COLORPIECE;
|
|
}
|
|
else
|
|
{
|
|
return TETRIS_VIEW_COLORPAUSE;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* redraws the dump and the falling piece (if necessary)
|
|
* @param pV pointer to the view on which the dump should be drawn
|
|
*/
|
|
void tetris_view_drawDump(tetris_view_t *pV)
|
|
{
|
|
assert(pV->pBucket != NULL);
|
|
if (tetris_bucket_getRow(pV->pBucket) <= -4)
|
|
{
|
|
return;
|
|
}
|
|
|
|
tetris_bearing_t nBearing =
|
|
pV->pVariantMethods->getBearing(pV->pVariant);
|
|
|
|
int8_t nPieceRow = tetris_bucket_getRow(pV->pBucket);
|
|
uint16_t nRowMap;
|
|
uint16_t nElementMask;
|
|
|
|
tetris_bucket_status_t status = tetris_bucket_getStatus(pV->pBucket);
|
|
for (int8_t nRow = TETRIS_VIEW_HEIGHT_DUMP - 1; nRow >= 0; --nRow)
|
|
{
|
|
nRowMap = tetris_bucket_getDumpRow(pV->pBucket, nRow);
|
|
|
|
// if a piece is hovering or gliding it needs to be drawn
|
|
if ((status == TETRIS_BUS_HOVERING) || (status == TETRIS_BUS_GLIDING) ||
|
|
(status == TETRIS_BUS_GAMEOVER))
|
|
{
|
|
if ((nRow >= nPieceRow) && (nRow <= nPieceRow + 3))
|
|
{
|
|
int8_t y = nRow - nPieceRow;
|
|
int8_t nColumn = tetris_bucket_getColumn(pV->pBucket);
|
|
uint16_t nPieceMap =
|
|
tetris_piece_getBitmap(tetris_bucket_getPiece(pV->pBucket));
|
|
// clear all bits of the piece we are not interested in and
|
|
// align the remaining row to LSB
|
|
nPieceMap = (nPieceMap & (0x000F << (y << 2))) >> (y << 2);
|
|
// shift remaining part to current column
|
|
if (nColumn >= 0)
|
|
{
|
|
nPieceMap <<= nColumn;
|
|
}
|
|
else
|
|
{
|
|
nPieceMap >>= -nColumn;
|
|
}
|
|
// cut off unwanted stuff
|
|
// nPieceMap &= 0x03ff;
|
|
// finally embed piece into the view
|
|
nRowMap |= nPieceMap;
|
|
}
|
|
}
|
|
|
|
nElementMask = 0x0001;
|
|
|
|
for (int8_t x = 0; x < TETRIS_VIEW_WIDTH_DUMP; ++x)
|
|
{
|
|
unsigned char nColor;
|
|
if ((nRowMap & nElementMask) != 0)
|
|
{
|
|
nColor = tetris_view_getPieceColor(pV);
|
|
}
|
|
else
|
|
{
|
|
nColor = TETRIS_VIEW_COLORSPACE;
|
|
}
|
|
tetris_view_setpixel(nBearing, TETRIS_VIEW_XOFFSET_DUMP + x,
|
|
TETRIS_VIEW_YOFFSET_DUMP + nRow, nColor);
|
|
nElementMask <<= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef TETRIS_VIEW_XOFFSET_PREVIEW
|
|
/**
|
|
* redraws the preview window
|
|
* @param pV pointer to the view on which the piece should be drawn
|
|
* @param pPc pointer to the piece for the preview window (may be NULL)
|
|
*/
|
|
void tetris_view_drawPreviewPiece(tetris_view_t *pV, tetris_piece_t *pPc)
|
|
{
|
|
tetris_bearing_t nBearing =
|
|
pV->pVariantMethods->getBearing(pV->pVariant);
|
|
|
|
if (pPc != NULL)
|
|
{
|
|
uint8_t nColor;
|
|
uint16_t nElementMask = 0x0001;
|
|
uint16_t nPieceMap;
|
|
if (pV->modeCurrent == TETRIS_VIMO_RUNNING)
|
|
{
|
|
nPieceMap = tetris_piece_getBitmap(pPc);
|
|
}
|
|
else
|
|
{
|
|
// an iconized "P"
|
|
nPieceMap = 0x26a6;
|
|
}
|
|
|
|
for (uint8_t y = 0; y < 4; ++y)
|
|
{
|
|
for (uint8_t x = 0; x < 4; ++x)
|
|
{
|
|
if ((nPieceMap & nElementMask) != 0)
|
|
{
|
|
nColor = TETRIS_VIEW_COLORPIECE;
|
|
}
|
|
else
|
|
{
|
|
nColor = TETRIS_VIEW_COLORSPACE;
|
|
}
|
|
tetris_view_setpixel(nBearing,
|
|
TETRIS_VIEW_XOFFSET_PREVIEW + x,
|
|
TETRIS_VIEW_YOFFSET_PREVIEW + y,
|
|
nColor);
|
|
nElementMask <<= 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (uint8_t y = 0; y < 4; ++y)
|
|
{
|
|
for (uint8_t x = 0; x < 4; ++x)
|
|
{
|
|
tetris_view_setpixel(nBearing,
|
|
TETRIS_VIEW_XOFFSET_PREVIEW + x,
|
|
TETRIS_VIEW_YOFFSET_PREVIEW + y,
|
|
TETRIS_VIEW_COLORSPACE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* draws borders in the given color
|
|
* @param pV pointer to the view on which the borders should be drawn
|
|
* @param nColor the color for the border
|
|
*/
|
|
void tetris_view_drawBorders(tetris_view_t *pV,
|
|
uint8_t nColor)
|
|
{
|
|
tetris_bearing_t nBearing =
|
|
pV->pVariantMethods->getBearing(pV->pVariant);
|
|
|
|
#if TETRIS_VIEW_YOFFSET_DUMP != 0
|
|
// fill upper space if required
|
|
for (uint8_t y = 0; y < TETRIS_VIEW_YOFFSET_DUMP; ++y)
|
|
{
|
|
tetris_view_drawHLine(nBearing, 0, VIEWCOLS - 1, y, nColor);
|
|
}
|
|
#endif
|
|
|
|
#if VIEWROWS > TETRIS_VIEW_HEIGHT_DUMP
|
|
// fill lower space if required
|
|
uint8_t y = TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP;
|
|
for (; y < VIEWROWS; ++y)
|
|
{
|
|
tetris_view_drawHLine(nBearing, 0, VIEWCOLS - 1, y, nColor);
|
|
}
|
|
#endif
|
|
|
|
#if TETRIS_VIEW_XOFFSET_DUMP != 0
|
|
// fill left space if required
|
|
for (uint8_t x = 0; x < TETRIS_VIEW_XOFFSET_DUMP; ++x)
|
|
{
|
|
tetris_view_drawVLine(nBearing, x, TETRIS_VIEW_YOFFSET_DUMP,
|
|
TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor);
|
|
}
|
|
#endif
|
|
|
|
#if VIEWCOLS > 16
|
|
// fill right space if required
|
|
uint8_t x = TETRIS_VIEW_XOFFSET_DUMP + TETRIS_VIEW_WIDTH_DUMP + 5;
|
|
for (; x < VIEWCOLS; ++x)
|
|
{
|
|
tetris_view_drawVLine(nBearing, x, TETRIS_VIEW_YOFFSET_DUMP,
|
|
TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef TETRIS_VIEW_XOFFSET_COUNTER
|
|
tetris_view_drawVLine(nBearing, TETRIS_VIEW_XOFFSET_COUNTER - 1,
|
|
TETRIS_VIEW_YOFFSET_DUMP,
|
|
TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor);
|
|
|
|
for (uint8_t x = TETRIS_VIEW_XOFFSET_COUNTER;
|
|
x < TETRIS_VIEW_XOFFSET_COUNTER + 3; ++x)
|
|
{
|
|
tetris_view_drawVLine(nBearing, x, TETRIS_VIEW_YOFFSET_DUMP,
|
|
TETRIS_VIEW_YOFFSET_COUNT100 - 1, nColor);
|
|
tetris_view_drawVLine(nBearing, x, TETRIS_VIEW_YOFFSET_PREVIEW + 4,
|
|
TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor);
|
|
}
|
|
|
|
tetris_view_drawVLine(nBearing, TETRIS_VIEW_XOFFSET_COUNTER + 3,
|
|
TETRIS_VIEW_YOFFSET_DUMP, TETRIS_VIEW_YOFFSET_COUNT1 + 3, nColor);
|
|
|
|
tetris_view_drawVLine(nBearing, TETRIS_VIEW_XOFFSET_COUNTER + 3,
|
|
TETRIS_VIEW_YOFFSET_PREVIEW + 4,
|
|
TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor);
|
|
|
|
tetris_view_drawHLine(nBearing, TETRIS_VIEW_XOFFSET_COUNTER,
|
|
TETRIS_VIEW_XOFFSET_COUNTER + 3, TETRIS_VIEW_YOFFSET_COUNT100 + 1,
|
|
nColor);
|
|
|
|
tetris_view_drawHLine(nBearing, TETRIS_VIEW_XOFFSET_COUNTER,
|
|
TETRIS_VIEW_XOFFSET_COUNTER + 3, TETRIS_VIEW_YOFFSET_COUNT10 + 3,
|
|
nColor);
|
|
|
|
tetris_view_drawHLine(nBearing, TETRIS_VIEW_XOFFSET_COUNTER,
|
|
TETRIS_VIEW_XOFFSET_COUNTER + 3, TETRIS_VIEW_YOFFSET_COUNT1 + 3,
|
|
nColor);
|
|
#elif defined TETRIS_VIEW_XOFFSET_PREVIEW
|
|
tetris_view_drawVLine(nBearing, TETRIS_VIEW_XOFFSET_PREVIEW - 1,
|
|
TETRIS_VIEW_YOFFSET_DUMP,
|
|
TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor);
|
|
|
|
for (uint8_t x = TETRIS_VIEW_XOFFSET_PREVIEW;
|
|
x < TETRIS_VIEW_XOFFSET_PREVIEW + 4; ++x)
|
|
{
|
|
tetris_view_drawVLine(nBearing, x, TETRIS_VIEW_YOFFSET_DUMP,
|
|
TETRIS_VIEW_YOFFSET_PREVIEW - 1, nColor);
|
|
tetris_view_drawVLine(nBearing, x, TETRIS_VIEW_YOFFSET_PREVIEW + 4,
|
|
TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor);
|
|
}
|
|
#elif TETRIS_VIEW_WIDTH_DUMP < VIEWCOLS
|
|
for (uint8_t x = TETRIS_VIEW_XOFFSET_DUMP + TETRIS_VIEW_WIDTH_DUMP;
|
|
x < VIEWCOLS; ++x)
|
|
{
|
|
tetris_view_drawVLine(nBearing, x, TETRIS_VIEW_YOFFSET_DUMP,
|
|
TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* lets the borders blink to notify player of a level change
|
|
* @param pV pointer to the view whose borders should blink
|
|
*/
|
|
void tetris_view_blinkBorders(tetris_view_t *pV)
|
|
{
|
|
for (uint8_t i = 0; i < TETRIS_VIEW_BORDER_BLINK_COUNT; ++i)
|
|
{
|
|
tetris_view_drawBorders(pV, TETRIS_VIEW_COLORPIECE);
|
|
WAIT(TETRIS_VIEW_BORDER_BLINK_DELAY);
|
|
tetris_view_drawBorders(pV, TETRIS_VIEW_COLORBORDER);
|
|
WAIT(TETRIS_VIEW_BORDER_BLINK_DELAY);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* lets complete lines blink to emphasize their removal
|
|
* @param pV pointer to the view whose complete lines should blink
|
|
*/
|
|
void tetris_view_blinkLines(tetris_view_t *pV)
|
|
{
|
|
|
|
// reduce necessity of pointer arithmetic
|
|
int8_t nRow = tetris_bucket_getRow(pV->pBucket);
|
|
uint8_t nRowMask = tetris_bucket_getRowMask(pV->pBucket);
|
|
|
|
tetris_bearing_t nBearing =
|
|
pV->pVariantMethods->getBearing(pV->pVariant);
|
|
|
|
// don't try to draw below the border
|
|
int8_t nDeepestRowOffset = ((nRow + 3) < TETRIS_VIEW_HEIGHT_DUMP ?
|
|
3 : TETRIS_VIEW_HEIGHT_DUMP - (nRow + 1));
|
|
|
|
// this loop controls how often the lines should blink
|
|
for (uint8_t i = 0; i < TETRIS_VIEW_LINE_BLINK_COUNT; ++i)
|
|
{
|
|
// this loop determines the color of the line to be drawn
|
|
for (uint8_t nColIdx = 0; nColIdx < 2; ++nColIdx)
|
|
{
|
|
// iterate through the possibly complete lines
|
|
for (uint8_t j = 0; j <= nDeepestRowOffset; ++j)
|
|
{
|
|
// is current line a complete line?
|
|
if ((nRowMask & (0x01 << j)) != 0)
|
|
{
|
|
// draw line in current color
|
|
uint8_t y = nRow + j;
|
|
for (uint8_t x = 0; x < 10; ++x)
|
|
{
|
|
|
|
uint8_t nColor = (nColIdx == 0 ? TETRIS_VIEW_COLORFADE
|
|
: TETRIS_VIEW_COLORPIECE);
|
|
// setpixel((pixel){14 - x, y}, nColor);
|
|
tetris_view_setpixel(nBearing,
|
|
TETRIS_VIEW_XOFFSET_DUMP + x,
|
|
TETRIS_VIEW_YOFFSET_DUMP + y,
|
|
nColor);
|
|
}
|
|
}
|
|
}
|
|
// wait a few ms to make the blink effect visible
|
|
WAIT(TETRIS_VIEW_LINE_BLINK_DELAY);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef TETRIS_VIEW_XOFFSET_COUNTER
|
|
/**
|
|
* displays completed Lines (0-99)
|
|
* @param pV pointer to the view
|
|
*/
|
|
void tetris_view_showLineNumbers(tetris_view_t *pV)
|
|
{
|
|
|
|
tetris_bearing_t nBearing =
|
|
pV->pVariantMethods->getBearing(pV->pVariant);
|
|
|
|
// get number of completed lines
|
|
uint16_t nLines = pV->pVariantMethods->getLines(pV->pVariant);
|
|
|
|
// get decimal places
|
|
int8_t nOnes = nLines % 10;
|
|
int8_t nTens = (nLines / 10) % 10;
|
|
int8_t nHundreds = (nLines / 100) % 10;
|
|
|
|
// draws the decimal places as 3x3 squares with 9 pixels
|
|
for (int i = 0, x = 0, y = 0; i < 9; ++i)
|
|
{
|
|
// pick drawing color for the ones
|
|
uint8_t nOnesPen = nOnes > i ?
|
|
TETRIS_VIEW_COLORCOUNTER : TETRIS_VIEW_COLORSPACE;
|
|
tetris_view_setpixel(nBearing, TETRIS_VIEW_XOFFSET_COUNTER + x,
|
|
TETRIS_VIEW_YOFFSET_COUNT1 + y, nOnesPen);
|
|
|
|
// pick drawing color for the tens
|
|
uint8_t nTensPen = nTens > i ?
|
|
TETRIS_VIEW_COLORCOUNTER : TETRIS_VIEW_COLORSPACE;
|
|
tetris_view_setpixel(nBearing, TETRIS_VIEW_XOFFSET_COUNTER + x,
|
|
TETRIS_VIEW_YOFFSET_COUNT10 + y, nTensPen);
|
|
|
|
// a maximum of 399 lines can be displayed
|
|
if (i < 3)
|
|
{
|
|
// pick drawing color for the hundreds
|
|
uint8_t nHundredsPen = nHundreds > i ?
|
|
TETRIS_VIEW_COLORCOUNTER : TETRIS_VIEW_COLORSPACE;
|
|
tetris_view_setpixel(nBearing, TETRIS_VIEW_XOFFSET_COUNTER + x,
|
|
TETRIS_VIEW_YOFFSET_COUNT100 + y, nHundredsPen);
|
|
|
|
}
|
|
|
|
// wrap lines if required
|
|
if ((++x % 3) == 0)
|
|
{
|
|
++y;
|
|
x = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
* unpacks the champion's initials from the uint16_t packed form
|
|
* @param nHighscoreName the champion's initials packed into a uint16_t
|
|
* @param pszName pointer to an array of char for the unpacked initials
|
|
*/
|
|
void tetris_view_formatHighscoreName(uint16_t nHighscoreName,
|
|
char *pszName)
|
|
{
|
|
pszName[0] = ((nHighscoreName >> 10) & 0x1F) + 65;
|
|
if (pszName[0] == '_')
|
|
{
|
|
pszName[0] = ' ';
|
|
}
|
|
|
|
pszName[1] = ((nHighscoreName >> 5) & 0x1F) + 65;
|
|
if (pszName[1] == '_')
|
|
{
|
|
pszName[1] = ' ';
|
|
}
|
|
|
|
pszName[2] = (nHighscoreName & 0x1F) + 65;
|
|
if (pszName[2] == '_')
|
|
{
|
|
pszName[2] = ' ';
|
|
}
|
|
|
|
pszName[3] = '\0';
|
|
}
|
|
/*@}*/
|
|
|
|
|
|
/****************************
|
|
* construction/destruction *
|
|
****************************/
|
|
|
|
tetris_view_t *tetris_view_construct(tetris_variant_t const *const pVarMethods,
|
|
void *pVariantData,
|
|
tetris_bucket_t *pBucket)
|
|
{
|
|
// memory allocation
|
|
assert((pVariantData != NULL) && (pBucket != NULL));
|
|
tetris_view_t *pView =
|
|
(tetris_view_t *) malloc(sizeof(tetris_view_t));
|
|
assert(pView != NULL);
|
|
|
|
// init
|
|
memset(pView, 0, sizeof(tetris_view_t));
|
|
pView->pVariantMethods = pVarMethods;
|
|
pView->pVariant = pVariantData;
|
|
pView->pBucket = pBucket;
|
|
pView->modeCurrent = pView->modeOld = TETRIS_VIMO_RUNNING;
|
|
|
|
// drawing some first stuff
|
|
clear_screen(0);
|
|
tetris_view_drawBorders(pView, TETRIS_VIEW_COLORBORDER);
|
|
|
|
return pView;
|
|
}
|
|
|
|
|
|
void tetris_view_destruct(tetris_view_t *pView)
|
|
{
|
|
assert(pView != NULL);
|
|
free(pView);
|
|
}
|
|
|
|
|
|
/***************************
|
|
* view related functions *
|
|
***************************/
|
|
|
|
void tetris_view_getDimensions(int8_t *w,
|
|
int8_t *h)
|
|
{
|
|
assert((w != NULL) && (h != NULL));
|
|
*w = TETRIS_VIEW_WIDTH_DUMP;
|
|
*h = TETRIS_VIEW_HEIGHT_DUMP;
|
|
}
|
|
|
|
|
|
void tetris_view_setViewMode(tetris_view_t *pV, tetris_view_mode_t vm)
|
|
{
|
|
pV->modeOld = pV->modeCurrent;
|
|
pV->modeCurrent = vm;
|
|
}
|
|
|
|
|
|
void tetris_view_update(tetris_view_t *pV)
|
|
{
|
|
assert(pV != NULL);
|
|
|
|
tetris_view_drawBorders(pV, TETRIS_VIEW_COLORBORDER);
|
|
|
|
#ifdef TETRIS_VIEW_XOFFSET_PREVIEW
|
|
// draw preview piece
|
|
tetris_view_drawPreviewPiece(pV,
|
|
pV->pVariantMethods->getPreviewPiece(pV->pVariant));
|
|
#endif
|
|
|
|
// let complete lines blink (if there are any)
|
|
if (tetris_bucket_getRowMask(pV->pBucket) != 0)
|
|
{
|
|
tetris_view_blinkLines(pV);
|
|
}
|
|
|
|
#ifdef TETRIS_VIEW_XOFFSET_COUNTER
|
|
// update line counter
|
|
tetris_view_showLineNumbers(pV);
|
|
#endif
|
|
|
|
// draw dump
|
|
tetris_view_drawDump(pV);
|
|
|
|
// visual feedback to inform about a level change
|
|
uint8_t nLevel = pV->pVariantMethods->getLevel(pV->pVariant);
|
|
if (nLevel != pV->nOldLevel)
|
|
{
|
|
tetris_view_blinkBorders(pV);
|
|
pV->nOldLevel = nLevel;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void tetris_view_showResults(tetris_view_t *pV)
|
|
{
|
|
#ifdef SCROLLTEXT_SUPPORT
|
|
char pszResults[55], pszHighscoreName[4];
|
|
uint16_t nScore = pV->pVariantMethods->getScore(pV->pVariant);
|
|
uint16_t nHighscore = pV->pVariantMethods->getHighscore(pV->pVariant);
|
|
uint16_t nLines = pV->pVariantMethods->getLines(pV->pVariant);
|
|
uint16_t nHighscoreName =
|
|
pV->pVariantMethods->getHighscoreName(pV->pVariant);
|
|
tetris_view_formatHighscoreName(nHighscoreName, pszHighscoreName);
|
|
|
|
if (nScore <= nHighscore)
|
|
{
|
|
snprintf(pszResults, sizeof(pszResults),
|
|
"</#Lines %u Score %u Highscore %u (%s)",
|
|
nLines, nScore, nHighscore, pszHighscoreName);
|
|
}
|
|
else
|
|
{
|
|
snprintf(pszResults, sizeof(pszResults),
|
|
"</#Lines %u New Highscore %u", nLines, nScore);
|
|
}
|
|
scrolltext(pszResults);
|
|
#endif
|
|
}
|