From 3f2d878b7daeba908a85f058a5158de3cb6d8606 Mon Sep 17 00:00:00 2001 From: Christian Kroll Date: Thu, 1 Apr 2010 03:11:59 +0000 Subject: [PATCH] vastly improved modularization and the view is resizable now --- config.in | 2 +- games/config.in | 9 +- games/games.mk | 2 +- games/tetris/Makefile | 17 +- games/tetris/bast.c | 254 ------------ games/tetris/bast.h | 77 ---- games/tetris/highscore.c | 60 ++- games/tetris/highscore.h | 53 ++- games/tetris/input.c | 425 ++++++++++---------- games/tetris/input.h | 183 ++++++--- games/tetris/logic.c | 721 ---------------------------------- games/tetris/logic.h | 190 --------- games/tetris/orientation.h | 13 + games/tetris/piece.c | 52 +-- games/tetris/piece.h | 129 +++--- games/tetris/playfield.c | 149 ++----- games/tetris/playfield.h | 256 ++++++------ games/tetris/tetris_main.c | 205 ++++++++++ games/tetris/tetris_main.h | 14 + games/tetris/tetrisfp.h | 6 - games/tetris/variant_bastet.c | 416 ++++++++++++++++++++ games/tetris/variant_bastet.h | 218 ++++++++++ games/tetris/variant_fp.c | 94 +++++ games/tetris/variant_fp.h | 41 ++ games/tetris/variant_std.c | 273 +++++++++++++ games/tetris/variant_std.h | 186 +++++++++ games/tetris/variants.h | 142 +++++++ games/tetris/view.c | 644 ++++++++++++++++++------------ games/tetris/view.h | 95 ++--- 29 files changed, 2743 insertions(+), 2183 deletions(-) delete mode 100644 games/tetris/bast.c delete mode 100644 games/tetris/bast.h delete mode 100644 games/tetris/logic.c delete mode 100644 games/tetris/logic.h create mode 100644 games/tetris/orientation.h create mode 100644 games/tetris/tetris_main.c create mode 100644 games/tetris/tetris_main.h delete mode 100644 games/tetris/tetrisfp.h create mode 100644 games/tetris/variant_bastet.c create mode 100644 games/tetris/variant_bastet.h create mode 100644 games/tetris/variant_fp.c create mode 100644 games/tetris/variant_fp.h create mode 100644 games/tetris/variant_std.c create mode 100644 games/tetris/variant_std.h create mode 100644 games/tetris/variants.h diff --git a/config.in b/config.in index fd8165b..cad3a79 100644 --- a/config.in +++ b/config.in @@ -71,7 +71,7 @@ comment "Animations" dep_bool "Feuer" ANIMATION_FEUER $RANDOM_SUPPORT dep_bool "Matrix" ANIMATION_MATRIX $RANDOM_SUPPORT dep_bool "Random Bright" ANIMATION_RANDOM_BRIGHT $RANDOM_SUPPORT - dep_bool "Stonefly" ANIMATION_STONEFLY $RANDOM_SUPPORT $GAME_TETRIS + dep_bool "Stonefly" ANIMATION_STONEFLY $RANDOM_SUPPORT $GAME_TETRIS_CORE dep_bool "Flying Dots" ANIMATION_FLYINGDOTS $RANDOM_SUPPORT dep_bool "Game of Life" ANIMATION_GAMEOFLIFE $RANDOM_SUPPORT bool "M Herweg" ANIMATION_MHERWEG diff --git a/games/config.in b/games/config.in index 4acc79c..5405dbe 100644 --- a/games/config.in +++ b/games/config.in @@ -1,9 +1,12 @@ mainmenu_option next_comment comment "Games" -dep_bool "tetris" GAME_TETRIS $JOYSTICK_SUPPORT $RANDOM_SUPPORT -dep_bool "bastet" GAME_BASTET $GAME_TETRIS -dep_bool "first person tetris" GAME_TETRIS_FP $GAME_TETRIS +dep_bool_menu "Tetris Core" GAME_TETRIS_CORE $JOYSTICK_SUPPORT $RANDOM_SUPPORT +dep_bool "Standard Tetris" GAME_TETRIS $GAME_TETRIS_CORE +dep_bool "Bastard Tetris" GAME_BASTET $GAME_TETRIS_CORE +dep_bool "First Person Tetris" GAME_TETRIS_FP $GAME_TETRIS_CORE +endmenu + dep_bool "space invaders" GAME_SPACE_INVADERS $JOYSTICK_SUPPORT $RANDOM_SUPPORT dep_bool "snake" GAME_SNAKE $JOYSTICK_SUPPORT $RANDOM_SUPPORT dep_bool "breakout" GAME_BREAKOUT $JOYSTICK_SUPPORT $RANDOM_SUPPORT diff --git a/games/games.mk b/games/games.mk index 3ce9167..91ee7b9 100644 --- a/games/games.mk +++ b/games/games.mk @@ -1,5 +1,5 @@ -ifeq ($(GAME_TETRIS),y) +ifeq ($(GAME_TETRIS_CORE),y) SUBDIRS += games/tetris endif diff --git a/games/tetris/Makefile b/games/tetris/Makefile index e27d714..1c909f8 100644 --- a/games/tetris/Makefile +++ b/games/tetris/Makefile @@ -3,10 +3,21 @@ TOPDIR = ../.. include $(TOPDIR)/defaults.mk +SRC = tetris_main.c piece.c playfield.c view.c input.c highscore.c + +ifeq ($(GAME_TETRIS),y) + SRC += variant_std.c +endif + ifeq ($(GAME_BASTET),y) - SRC = piece.c playfield.c view.c logic.c input.c highscore.c bast.c -else - SRC = piece.c playfield.c view.c logic.c input.c highscore.c + SRC += variant_bastet.c +endif + +ifeq ($(GAME_TETRIS_FP),y) + ifneq ($(GAME_TETRIS),y) + SRC += variant_std.c + endif + SRC += variant_fp.c endif include $(TOPDIR)/rules.mk diff --git a/games/tetris/bast.c b/games/tetris/bast.c deleted file mode 100644 index 5d8a01b..0000000 --- a/games/tetris/bast.c +++ /dev/null @@ -1,254 +0,0 @@ -#include -#include -#include -#include "../../random/prng.h" -#include "piece.h" -#include "playfield.h" -#include "bast.h" - - -/*************************** - * non-interface functions * - ***************************/ - -/* Function: tetris_bastet_clearColHeights; - * Description: resets the array for the column heights - * Argument pBastet: bastet instance whose array should be reset - * Argument nStart: start index - * Argument nStop: stop index - * Return value: void - */ -void tetris_bastet_clearColHeights(tetris_bastet_t *pBastet, - int8_t nStart, - int8_t nStop) -{ - for (int i = nStart; i <= nStop; ++i) - { - pBastet->pColHeights[i] = 0; - } -} - - -/* Function: tetris_bastet_qsortCompare - * Description: compare function for quick sorting the pieces by score - * Argument pa: the first value to compare - * Argument pb: the second value to compare - * Return value: void - */ -int tetris_bastet_qsortCompare(const void *pa, const void *pb) -{ - tetris_bastet_scorepair_t *pScorePairA = (tetris_bastet_scorepair_t *)pa; - tetris_bastet_scorepair_t *pScorePairB = (tetris_bastet_scorepair_t *)pb; - if (pScorePairA->nScore == pScorePairB->nScore) - { - return 0; - } - else if (pScorePairA->nScore < pScorePairB->nScore) - { - return -1; - } - else - { - return 1; - } -} - - -/**************************** - * construction/destruction * - ****************************/ - -/* Function: tetris_bastet_construct - * Description: constructs a bastet instance for a given playfield - * Argument pPlayfield: the playfield to be observed - * Return value: pointer to a newly created bastet instance - */ -tetris_bastet_t* tetris_bastet_construct(tetris_playfield_t *pPl) -{ - tetris_bastet_t *pBastet = - (tetris_bastet_t *) malloc(sizeof(tetris_bastet_t)); - - pBastet->pPlayfield = pPl; - - int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); - pBastet->pColHeights = (int8_t*) calloc(nWidth, sizeof(int8_t)); - tetris_bastet_clearColHeights(pBastet, 0, nWidth - 1); - - return pBastet; -} - - -/* Function: tetris_bastet_destruct - * Description: destructs the given bastet instance - * Argument pBastet: the bastet instance to be destroyed - * Return value: void - */ -void tetris_bastet_destruct(tetris_bastet_t *pBastet) -{ - if (pBastet->pColHeights != NULL) - { - free(pBastet->pColHeights); - } - - free(pBastet); -} - - -/**************************** - * bastet related functions * - ****************************/ - -/* Function: tetris_bastet_construct - * Description: calculates a score for a piece at a given column - * Argument pBastet: the bastet instance of interest - * Argument pPiece: the piece to be tested - * Argument pnColum: the column where the piece should be dropped - * Return value: score for the given move - */ -int16_t tetris_bastet_evalPos(tetris_bastet_t *pBastet, - tetris_piece_t *pPiece, - int8_t nColumn) -{ - // the row where the given piece collides - int8_t nDeepestRow = tetris_playfield_predictDeepestRow(pBastet->pPlayfield, - pPiece, nColumn); - - // initial score of the given piece - int16_t nScore = -32000; - - // modify score based on complete lines - int8_t nLines = tetris_playfield_predictCompleteLines(pBastet->pPlayfield, - pPiece, nDeepestRow, nColumn); - nScore += 5000 * nLines; - - // determine sane start and stop columns whose heights we want to calculate - int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); - int8_t nStartCol = ((nColumn - 1) < 0) ? 0 : nColumn - 1; - int8_t nStopCol; - // Do we start at the left most position? - // If we do we MUST calculate the heights of ALL columns (initial step) - if (nColumn <= -3) - { - nStopCol = nWidth - 1; - // reset all column heights to zero - tetris_bastet_clearColHeights(pBastet, 0 , nWidth); - } - // If not, only calculate columns which are affected by the moved piece. - else - { - nStopCol = (nColumn + 3) < nWidth ? nColumn + 3 : nWidth - 1; - // clear affected column heights to prevent miscalculations - tetris_bastet_clearColHeights(pBastet, nStartCol, nStopCol); - } - - // go through every row and calculate column heights - tetris_playfield_iterator_t iterator; - int8_t nHeight = 1; - uint16_t *pDump = tetris_playfield_predictBottomRow(&iterator, - pBastet->pPlayfield, pPiece, nDeepestRow, nColumn); - if (pDump == NULL) - { - // an immediately returned NULL is caused by a full dump -> low score - return -32766; - } - while (pDump != NULL) - { - uint16_t nColMask = 0x0001 << nStartCol; - for (int x = nStartCol; x <= nStopCol; ++x) - { - if ((*pDump & nColMask) != 0) - { - pBastet->pColHeights[x] = nHeight; - } - nColMask <<= 1; - } - pDump = tetris_playfield_predictNextRow(&iterator); - ++nHeight; - } - // modify score based on predicted column heights - for (int x = 0; x < nWidth; ++x) - { - nScore -= 5 * pBastet->pColHeights[x]; - } - - return nScore; -} - - -/* Function: tetris_bastet_minimax - * Description: calculates the best possible score for every piece - * Argument pBastet: the bastet instance of interest - * Return value: void - */ -void tetris_bastet_minimax(tetris_bastet_t *pBastet) -{ - int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); - tetris_piece_t *pPiece = tetris_piece_construct(TETRIS_PC_LINE, - TETRIS_PC_ANGLE_0); - for (int8_t nBlock = TETRIS_PC_LINE; nBlock <= TETRIS_PC_Z; ++nBlock) - { - int16_t nMaxScore = -32768; - tetris_piece_changeShape(pPiece, nBlock); - int8_t nAngleCount = tetris_piece_angleCount(pPiece); - for (int8_t nAngle = TETRIS_PC_ANGLE_0; nAngle < nAngleCount; ++nAngle) - { - tetris_piece_changeAngle(pPiece, nAngle); - for (int8_t nCol = -3; nCol < nWidth; ++nCol) - { - int16_t nScore = tetris_bastet_evalPos(pBastet, pPiece, nCol); - nMaxScore = nMaxScore > nScore ? nMaxScore : nScore; - } - } - pBastet->nPieceScores[nBlock].shape = nBlock; - pBastet->nPieceScores[nBlock].nScore = nMaxScore; - } - tetris_piece_destruct(pPiece); -} - - -/* Function: tetris_bastet_choosePiece - * Description: calculates the worst possible piece - * Argument pBastet: the bastet instance of interest - * Return value: the worst possible piece - */ -tetris_piece_t* tetris_bastet_choosePiece(tetris_bastet_t *pBastet) -{ - const uint8_t nPercent[7] = {75, 92, 98, 100, 100, 100, 100}; - tetris_bastet_minimax(pBastet); - - // perturb score (-2 to +2) to avoid stupid tie handling - for (uint8_t i = 0; i < 7; ++i) - { - pBastet->nPieceScores[i].nScore += random8() % 5 - 2; - } - - qsort(pBastet->nPieceScores, 7, sizeof(tetris_bastet_scorepair_t), - &tetris_bastet_qsortCompare); - - uint8_t nRnd = rand() % 100; - for (uint8_t i = 0; i < 7; i++) - { - if (nRnd < nPercent[i]) - { - return tetris_piece_construct(pBastet->nPieceScores[i].shape, - TETRIS_PC_ANGLE_0); - } - } - - //should not arrive here - return tetris_piece_construct(pBastet->nPieceScores[0].shape, - TETRIS_PC_ANGLE_0); -} - - -/* Function: tetris_bastet_choosePreviewPiece - * Description: returns the best possible piece - * (run tetris_bastet_choosePiece first!) - * Argument pBastet: the bastet instance of interest - * Return value: the worst possible piece - */ -tetris_piece_t* tetris_bastet_choosePreviewPiece(tetris_bastet_t *pBastet) -{ - return tetris_piece_construct(pBastet->nPieceScores[6].shape, - TETRIS_PC_ANGLE_0); -} diff --git a/games/tetris/bast.h b/games/tetris/bast.h deleted file mode 100644 index 4acc9cd..0000000 --- a/games/tetris/bast.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef BAST_H_ -#define BAST_H_ - -#include -#include "playfield.h" -#include "piece.h" - -/********* - * types * - *********/ - -typedef struct tetris_bastet_scorepair_t -{ - tetris_piece_shape_t shape; - int16_t nScore; -} -tetris_bastet_scorepair_t; - - -typedef struct tetris_bastet_t -{ - tetris_playfield_t *pPlayfield; // the playfield to be examined - int8_t *pColHeights; // array of calculated heights - tetris_bastet_scorepair_t nPieceScores[7]; // score for every piece -} -tetris_bastet_t; - - -/**************************** - * construction/destruction * - ****************************/ - -/* Function: tetris_bastet_construct - * Description: constructs a bastet instance for a given playfield - * Argument pPlayfield: the playfield to be observed - * Return value: pointer to a newly created bastet instance - */ -tetris_bastet_t* tetris_bastet_construct(tetris_playfield_t *pPl); - - -/* Function: tetris_bastet_destruct - * Description: destructs the given bastet instance - * Argument pBastet: the bastet instance to be destroyed - * Return value: void - */ -void tetris_bastet_destruct(tetris_bastet_t *pBastet); - - -/**************************** - * bastet related functions * - ****************************/ - -/* Function: tetris_bastet_construct - * Description: calculates a score for a piece at a given column - * Argument pBastet: the bastet instance of interest - * Argument pPiece: the piece to be tested - * Argument pnColum: the column where the piece should be dropped - * Return value: score for the given move - */ -int16_t tetris_bastet_evalPos(tetris_bastet_t *pBastet, - tetris_piece_t *pPiece, - int8_t nColumn); - - -/* Function: tetris_bastet_minimax - * Description: calculates the best possible score for every piece - * Argument pBastet: the bastet instance of interest - * Return value: void - */ -void tetris_bastet_minimax(); - - -tetris_piece_t* tetris_bastet_choosePiece(tetris_bastet_t *pBastet); - -tetris_piece_t* tetris_bastet_choosePreviewPiece(tetris_bastet_t *pBastet); - -#endif /* BAST_H_ */ diff --git a/games/tetris/highscore.c b/games/tetris/highscore.c index 9167b67..7b8001f 100644 --- a/games/tetris/highscore.c +++ b/games/tetris/highscore.c @@ -2,16 +2,19 @@ #include #include #include +#include "highscore.h" #include "../../config.h" #include "../../scrolltext/scrolltext.h" #include "../../joystick/joystick.h" -#include "highscore.h" +#include "../../compat/eeprom.h" + +// global array for the highscores +uint16_t tetris_highscore[TETRIS_HISCORE_END] EEMEM; + +// global array for the champions' initials +uint16_t tetris_highscore_name[TETRIS_HISCORE_END] EEMEM; -/* Function: tetris_highscore_inputName - * Description: let user input a three character name - * Return value: name packed into a uint16_t - */ uint16_t tetris_highscore_inputName(void) { #ifdef SCROLLTEXT_SUPPORT @@ -111,3 +114,50 @@ uint16_t tetris_highscore_inputName(void) return (0); #endif } + + +uint16_t tetris_highscore_retrieveHighscore(tetris_highscore_index_t nIndex) +{ + uint16_t nHighscore = 0; + nHighscore = eeprom_read_word(&tetris_highscore[nIndex]); + + // a score of 65535 is most likely caused by uninitialized EEPROM addresses + if (nHighscore == 65535) + { + nHighscore = 0; + } + + return nHighscore; +} + + +void tetris_highscore_saveHighscore(tetris_highscore_index_t nIndex, + uint16_t nHighscore) +{ + if (nHighscore > tetris_highscore_retrieveHighscore(nIndex)) + { + eeprom_write_word(&tetris_highscore[nIndex], nHighscore); + } +} + + +uint16_t tetris_highscore_retrieveHighscoreName(tetris_highscore_index_t nIdx) +{ + uint16_t nHighscoreName = 0; + nHighscoreName = eeprom_read_word(&tetris_highscore_name[nIdx]); + + // a score of 65535 is most likely caused by uninitialized EEPROM addresses + if (nHighscoreName == 65535) + { + nHighscoreName = 0; + } + + return nHighscoreName; +} + + +void tetris_highscore_saveHighscoreName(tetris_highscore_index_t nIndex, + uint16_t nHighscoreName) +{ + eeprom_write_word(&tetris_highscore_name[nIndex], nHighscoreName); +} diff --git a/games/tetris/highscore.h b/games/tetris/highscore.h index 99d4531..a14997d 100644 --- a/games/tetris/highscore.h +++ b/games/tetris/highscore.h @@ -1,10 +1,57 @@ #ifndef TETRIS_HIGHSCORE_H_ #define TETRIS_HIGHSCORE_H_ -/* Function: tetris_highscore_inputName - * Description: let user input a three character name - * Return value: name packed into a uint16_t +/** + * indexes for different tetris variants + */ +typedef enum tetris_highscore_index_t +{ + TETRIS_HISCORE_TETRIS, /**< highscore index for the standard variant */ + TETRIS_HISCORE_BASTET, /**< highscore index for the bastet variant */ + TETRIS_HISCORE_FP, /**< highscore index for the first person variant */ + TETRIS_HISCORE_PAD, /**< don't use (padding for an even array boundary)*/ + TETRIS_HISCORE_END /**< boundary for the highscore array */ +} tetris_highscore_index_t; + + +/** + * let user input a three character name + * @return name packed into a uint16_t */ uint16_t tetris_highscore_inputName(void); + +/** + * retrieves the highscore from storage + * @param nIndex the variant dependent index of the highscore + * @return the highscore + */ +uint16_t tetris_highscore_retrieveHighscore(tetris_highscore_index_t nIndex); + + +/** + * saves the highscore into the storage + * @param nIndex the variant dependent index of the highscore + * @param nHighscoreName the highscore + */ +void tetris_highscore_saveHighscore(tetris_highscore_index_t nIndex, + uint16_t nHighscore); + + +/** + * retrieves the initials of the champion from storage + * @param nIdx the variant dependent index of the highscore + * @return the initials of the champion packed into a uint16_t + */ +uint16_t tetris_highscore_retrieveHighscoreName(tetris_highscore_index_t nIdx); + + +/** + * saves the initials of the champion + * @param nIndex the variant dependent index of the highscore + * @param nHighscoreName the initials of the champion packed into a uint16_t + */ +void tetris_highscore_saveHighscoreName(tetris_highscore_index_t nIndex, + uint16_t nHighscoreName); + #endif /*TETRIS_HIGHSCORE_H_*/ diff --git a/games/tetris/input.c b/games/tetris/input.c index 258708a..d45e904 100644 --- a/games/tetris/input.c +++ b/games/tetris/input.c @@ -6,63 +6,79 @@ #include "../../joystick/joystick.h" #include "../../util.h" #include "input.h" - -#ifdef GAME_TETRIS_FP -#include "tetrisfp.h" -#endif +#include "orientation.h" #include "../../compat/pgmspace.h" #define WAIT(ms) wait(ms) #define PM(value) pgm_read_word(&value) +/** + * \defgroup TetrisInputDefinesPrivate Input: Internal constants + */ +/*@{*/ + /*********** * defines * ***********/ -// amount of milliseconds that each loop cycle waits +/** amount of milliseconds that each loop cycle waits */ #define TETRIS_INPUT_TICKS 5 -// amount of milliseconds the input is ignored after the pause combo has been -// pressed, since it is difficult to release all buttons simultaneously +/** + * amount of milliseconds the input is ignored after the pause combo has been + * pressed, since it is difficult to release all buttons simultaneously + */ #define TETRIS_INPUT_PAUSE_TICKS 100 -// amount of allowed loop cycles while in pause mode so that the game -// automatically continues after five minutes +/** + * amount of allowed loop cycles while in pause mode so that the game + * automatically continues after five minutes + */ #define TETRIS_INPUT_PAUSE_CYCLES 60000 -// minimum of cycles in gliding mode +/** minimum of cycles in gliding mode */ #define TETRIS_INPUT_GLIDE_CYCLES 75 -// here you can adjust the delays (in loop cycles) for key repeat +/** initial delay (in loop cycles) for key repeat */ #define TETRIS_INPUT_REPEAT_INITIALDELAY 35 +/** delay (in loop cycles) for key repeat */ #define TETRIS_INPUT_REPEAT_DELAY 5 -// Here you can adjust the amount of loop cycles a command is ignored after -// its button has been released (to reduce joystick chatter) -#define TETRIS_INPUT_CHATTER_TICKS_ROT_CW 24 -#define TETRIS_INPUT_CHATTER_TICKS_ROT_CCW 24 +/** amount of loop cyles the left button is ignored */ #define TETRIS_INPUT_CHATTER_TICKS_LEFT 12 +/** amount of loop cyles the right button is ignored */ #define TETRIS_INPUT_CHATTER_TICKS_RIGHT 12 +/** amount of loop cyles the down button is ignored */ #define TETRIS_INPUT_CHATTER_TICKS_DOWN 12 +/** amount of loop cyles the clockwise rotation button is ignored */ +#define TETRIS_INPUT_CHATTER_TICKS_ROT_CW 24 +/** amount of loop cyles the counter clockwise rotation button is ignored */ +#define TETRIS_INPUT_CHATTER_TICKS_ROT_CCW 24 +/** amount of loop cyles the drop button is ignored */ #define TETRIS_INPUT_CHATTER_TICKS_DROP 20 -// wait cycles per level (array of uint8_t) +/** wait cycles per level (array of uint8_t) */ #define TETRIS_INPUT_LVL_CYCLES 200, 133, 100, 80, 66, 57, 50, 44, 40, 36, 33, \ 30, 28, 26, 25, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9 +/*@}*/ + +/** + * \defgroup TetrisInputNoInterface Input: Internal non-interface functions + */ +/*@{*/ /*************************** * non-interface functions * ***************************/ -/* Function: tetris_input_chatterProtect; - * Description: sets an ignore counter to a command specific value if it is 0 - * Argument pIn: pointer to an input object - * Argument cmd: the command whose counter should be set - * Return value: void +/** + * sets an ignore counter to a command specific value if it is 0 + * @param pIn pointer to an input object + * @param cmd the command whose counter should be set */ -void tetris_input_chatterProtect (tetris_input_t *pIn, - tetris_input_command_t cmd) +void tetris_input_chatterProtect(tetris_input_t *pIn, + tetris_input_command_t cmd) { // never exceed the index assert(cmd < TETRIS_INCMD_NONE); @@ -71,11 +87,11 @@ void tetris_input_chatterProtect (tetris_input_t *pIn, // released (every command has its own counter) static const uint8_t nInitialIgnoreValue[TETRIS_INCMD_NONE] PROGMEM = { - TETRIS_INPUT_CHATTER_TICKS_ROT_CW, - TETRIS_INPUT_CHATTER_TICKS_ROT_CCW, TETRIS_INPUT_CHATTER_TICKS_LEFT, TETRIS_INPUT_CHATTER_TICKS_RIGHT, TETRIS_INPUT_CHATTER_TICKS_DOWN, + TETRIS_INPUT_CHATTER_TICKS_ROT_CW, + TETRIS_INPUT_CHATTER_TICKS_ROT_CCW, TETRIS_INPUT_CHATTER_TICKS_DROP, 0, // TETRIS_INCMD_GRAVITY (irrelevant because it doesn't have a button) 0 // TETRIS_INCMD_PAUSE (is a combination of ROT_CW and DOWN) @@ -116,101 +132,118 @@ void tetris_input_chatterProtect (tetris_input_t *pIn, } -/* Function: tetris_input_queryJoystick - * Description: translates joystick movements into tetris_input_command_t - * Argument pIn: pointer to an input object - * Return value: see definition of tetris_input_command_t +/** + * remaps tetris commands according to current orientation + * @param pIn pointer to an input object + * @param nCmd command which has to be mapped + * @return mapped tetris command + * @see tetris_input_command_t */ -tetris_input_command_t tetris_input_queryJoystick(uint8_t nFirstPerson) +tetris_input_command_t tetris_input_mapCommand(tetris_orientation_t nOrient, + tetris_input_command_t nCmd) { - tetris_input_command_t cmdReturn; + const tetris_input_command_t nMapping[] = + { + TETRIS_INCMD_DOWN, TETRIS_INCMD_ROT_CW, TETRIS_INCMD_RIGHT, + TETRIS_INCMD_LEFT, + TETRIS_INCMD_RIGHT, TETRIS_INCMD_LEFT, TETRIS_INCMD_ROT_CW, + TETRIS_INCMD_DOWN, + + TETRIS_INCMD_ROT_CW, TETRIS_INCMD_DOWN, TETRIS_INCMD_LEFT, + TETRIS_INCMD_RIGHT + }; + + return (nOrient == TETRIS_ORIENTATION_0) || (nCmd >= TETRIS_INCMD_ROT_CCW) ? + nCmd : (nMapping[(nOrient - 1) * 4 + nCmd]); +} + + +/** + * translates joystick movements into tetris commands + * @return interpreted joystick command + * @see tetris_input_command_t + */ +tetris_input_command_t tetris_input_queryJoystick(tetris_input_t *pIn) +{ + // map port input to a tetris command + tetris_input_command_t cmdJoystick; if (JOYISFIRE) { - cmdReturn = TETRIS_INCMD_DROP; + cmdJoystick = TETRIS_INCMD_DROP; } else if (JOYISLEFT) { -#ifdef GAME_TETRIS_FP - switch (tetris_screendir) { - case 0: cmdReturn = TETRIS_INCMD_LEFT; break; - case 1: cmdReturn = TETRIS_INCMD_DOWN; break; - case 2: cmdReturn = TETRIS_INCMD_RIGHT; break; - case 3: cmdReturn = TETRIS_INCMD_ROT_CW; break; - } -#else - cmdReturn = TETRIS_INCMD_LEFT; -#endif - + cmdJoystick = TETRIS_INCMD_LEFT; } else if (JOYISRIGHT) { -#ifdef GAME_TETRIS_FP - switch (tetris_screendir) { - case 0: cmdReturn = TETRIS_INCMD_RIGHT; break; - case 1: cmdReturn = TETRIS_INCMD_ROT_CW; break; - case 2: cmdReturn = TETRIS_INCMD_LEFT; break; - case 3: cmdReturn = TETRIS_INCMD_DOWN; break; - } -#else - cmdReturn = TETRIS_INCMD_RIGHT; -#endif + cmdJoystick = TETRIS_INCMD_RIGHT; } else if (JOYISUP && JOYISDOWN) { - cmdReturn = TETRIS_INCMD_PAUSE; + cmdJoystick = TETRIS_INCMD_PAUSE; WAIT(TETRIS_INPUT_PAUSE_TICKS); } else if (JOYISDOWN) { -#ifdef GAME_TETRIS_FP - switch (tetris_screendir) { - case 0: cmdReturn = TETRIS_INCMD_DOWN; break; - case 1: cmdReturn = TETRIS_INCMD_RIGHT; break; - case 2: cmdReturn = TETRIS_INCMD_ROT_CW; break; - case 3: cmdReturn = TETRIS_INCMD_LEFT; break; - } -#else - cmdReturn = TETRIS_INCMD_DOWN; -#endif + cmdJoystick = TETRIS_INCMD_DOWN; } else if (JOYISUP) { -#ifdef GAME_TETRIS_FP - switch (tetris_screendir) { - case 0: cmdReturn = TETRIS_INCMD_ROT_CW; break; - case 1: cmdReturn = TETRIS_INCMD_LEFT; break; - case 2: cmdReturn = TETRIS_INCMD_DOWN; break; - case 3: cmdReturn = TETRIS_INCMD_RIGHT; break; - } -#else - cmdReturn = TETRIS_INCMD_ROT_CW; -#endif + cmdJoystick = TETRIS_INCMD_ROT_CW; } else { - cmdReturn = TETRIS_INCMD_NONE; + cmdJoystick = TETRIS_INCMD_NONE; } + // decrement all ignore counters + for (int nIgnIndex = 0; nIgnIndex < TETRIS_INCMD_NONE; ++nIgnIndex) + { + if (pIn->nIgnoreCmdCounter[nIgnIndex] != 0) + { + --pIn->nIgnoreCmdCounter[nIgnIndex]; + } + } + + // chatter protection + if (cmdJoystick < TETRIS_INCMD_NONE) + { + if (pIn->nIgnoreCmdCounter[cmdJoystick] == 0) + { + tetris_input_chatterProtect(pIn, cmdJoystick); + } + else if (cmdJoystick != pIn->cmdRawLast) + { + cmdJoystick = TETRIS_INCMD_NONE; + } + } + + // memorize current command (for detecting prolonged key presses) + pIn->cmdRawLast = cmdJoystick; + + tetris_input_command_t cmdReturn = + tetris_input_mapCommand(pIn->nOrientation, cmdJoystick); + // remap command according to current orientation return cmdReturn; } +/*@}*/ -/***************************** - * construction/destruction * - *****************************/ -/* Function: tetris_input_construct - * Description: constructs an input object for André's borg - * Return value: pointer to a newly created input object - */ -tetris_input_t *tetris_input_construct() +/**************************** + * construction/destruction * + ****************************/ + +tetris_input_t *tetris_input_construct(void) { tetris_input_t *pIn = (tetris_input_t *)malloc(sizeof(tetris_input_t)); assert(pIn != NULL); - pIn->cmdLast = TETRIS_INCMD_NONE; - pIn->nLevel = 0xFF; + pIn->cmdRawLast = pIn->cmdLast = TETRIS_INCMD_NONE; + pIn->nOrientation = TETRIS_ORIENTATION_0; + // pIn->nLevel = 0xFF; tetris_input_setLevel(pIn, 0); pIn->nLoopCycles = 0; pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY; @@ -221,11 +254,6 @@ tetris_input_t *tetris_input_construct() } -/* Function: tetris_input_destruct - * Description: destructs an input structure - * Argument pIn: pointer to the input object which should to be destructed - * Return value: void - */ void tetris_input_destruct(tetris_input_t *pIn) { assert(pIn != NULL); @@ -237,15 +265,8 @@ void tetris_input_destruct(tetris_input_t *pIn) * input related functions * ***************************/ -/* Function: tetris_input_getCommand - * Description: retrieves commands from joystick or loop interval - * Argument pIn: pointer to an input object - * Argument nPace: falling pace (see definition of tetris_input_pace_t) - * Return value: see definition of tetris_input_command_t - */ tetris_input_command_t tetris_input_getCommand(tetris_input_t *pIn, - tetris_input_pace_t nPace, - uint8_t nFirstPerson) + tetris_input_pace_t nPace) { assert (pIn != NULL); @@ -272,122 +293,96 @@ tetris_input_command_t tetris_input_getCommand(tetris_input_t *pIn, while (pIn->nLoopCycles < nMaxCycles) { - cmdJoystick = tetris_input_queryJoystick(nFirstPerson); + cmdJoystick = tetris_input_queryJoystick(pIn); - // only obey current command if it is not considered as chattering - if (((cmdJoystick < TETRIS_INCMD_NONE) ? - pIn->nIgnoreCmdCounter[cmdJoystick] : 0) == 0) + switch (cmdJoystick) { - switch (cmdJoystick) + case TETRIS_INCMD_LEFT: + case TETRIS_INCMD_RIGHT: + case TETRIS_INCMD_DOWN: + // only react if either the current command differs from the + // last one or enough loop cycles have been run on the same + // command (for key repeat) + if ((pIn->cmdLast != cmdJoystick) || ((pIn->cmdLast == cmdJoystick) + && (pIn->nRepeatCount >= TETRIS_INPUT_REPEAT_DELAY))) { - case TETRIS_INCMD_LEFT: - case TETRIS_INCMD_RIGHT: - case TETRIS_INCMD_DOWN: - // only react if either the current command differs from the - // last one or enough loop cycles have been run on the same - // command (for key repeat) - if ((pIn->cmdLast != cmdJoystick) - || ((pIn->cmdLast == cmdJoystick) - && (pIn->nRepeatCount >= TETRIS_INPUT_REPEAT_DELAY))) - { - // reset repeat counter - if (pIn->cmdLast != cmdJoystick) - { - // different command: we set an extra initial delay - pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY; - } - else - { - // same command: there's no extra initial delay - pIn->nRepeatCount = 0; - } - - // update cmdLast and return value - pIn->cmdLast = cmdReturn = cmdJoystick; - } - else - { - // if not enough loop cycles have been run we increment the - // repeat counter, ensure that we continue the loop and - // keep the key repeat functioning - ++pIn->nRepeatCount; - cmdReturn = TETRIS_INCMD_NONE; - } - break; - - case TETRIS_INCMD_DROP: - case TETRIS_INCMD_ROT_CW: - case TETRIS_INCMD_ROT_CCW: - // no key repeat here + // reset repeat counter if (pIn->cmdLast != cmdJoystick) { - pIn->cmdLast = cmdReturn = cmdJoystick; + // different command: we set an extra initial delay + pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY; } else { - // if we reach here the command is ignored - cmdReturn = TETRIS_INCMD_NONE; - } - break; - - case TETRIS_INCMD_PAUSE: - // if this is an initial pause command, make sure that the logic - // module is informed about it - if (pIn->cmdLast != TETRIS_INCMD_PAUSE) - { - pIn->cmdLast = cmdReturn = cmdJoystick; - pIn->nPauseCount = 0; - } - // consecutive pause commands should not cause the loop to leave - else - { - cmdReturn = TETRIS_INCMD_NONE; - } - break; - - case TETRIS_INCMD_NONE: - // chatter protection - if (pIn->cmdLast != TETRIS_INCMD_NONE) - { - tetris_input_chatterProtect(pIn, pIn->cmdLast); + // same command: there's no extra initial delay + pIn->nRepeatCount = 0; } - // If the game is paused (last command was TETRIS_INCMD_PAUSE) - // we ensure that the variable which holds that last command - // isn't touched. We use this as a flag so that the loop cycle - // counter doesn't get incremented. - // We count the number of pause cycles, though. If enough cycles - // have been run, we enforce the continuation of the game. - if ((pIn->cmdLast != TETRIS_INCMD_PAUSE) || - (++pIn->nPauseCount > TETRIS_INPUT_PAUSE_CYCLES)) - { - pIn->cmdLast = TETRIS_INCMD_NONE; - } - - // reset repeat counter - pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY; - - // using cmdReturn as a flag for not leaving the loop - cmdReturn = TETRIS_INCMD_NONE; - break; - - default: - break; + // update cmdLast and return value + pIn->cmdLast = cmdReturn = cmdJoystick; } - } - // current command is considered as chattering - else - { - pIn->cmdLast = cmdReturn = TETRIS_INCMD_NONE; - } - - // decrement all ignore counters - for (int nIgnIndex = 0; nIgnIndex < TETRIS_INCMD_NONE; ++nIgnIndex) - { - if (pIn->nIgnoreCmdCounter[nIgnIndex] != 0) + else { - --pIn->nIgnoreCmdCounter[nIgnIndex]; + // if not enough loop cycles have been run we increment the + // repeat counter, ensure that we continue the loop and + // keep the key repeat functioning + ++pIn->nRepeatCount; + cmdReturn = TETRIS_INCMD_NONE; } + break; + + case TETRIS_INCMD_DROP: + case TETRIS_INCMD_ROT_CW: + case TETRIS_INCMD_ROT_CCW: + // no key repeat here + if (pIn->cmdLast != cmdJoystick) + { + pIn->cmdLast = cmdReturn = cmdJoystick; + } + else + { + // if we reach here the command is ignored + cmdReturn = TETRIS_INCMD_NONE; + } + break; + + case TETRIS_INCMD_PAUSE: + // if this is an initial pause command, make sure that the logic + // module is informed about it + if (pIn->cmdLast != TETRIS_INCMD_PAUSE) + { + pIn->cmdLast = cmdReturn = cmdJoystick; + pIn->nPauseCount = 0; + } + // consecutive pause commands should not cause the loop to leave + else + { + cmdReturn = TETRIS_INCMD_NONE; + } + break; + + case TETRIS_INCMD_NONE: + // If the game is paused (last command was TETRIS_INCMD_PAUSE) + // we ensure that the variable which holds that last command + // isn't touched. We use this as a flag so that the loop cycle + // counter doesn't get incremented. + // We count the number of pause cycles, though. If enough cycles + // have been run, we enforce the continuation of the game. + if ((pIn->cmdLast != TETRIS_INCMD_PAUSE) || + (++pIn->nPauseCount > TETRIS_INPUT_PAUSE_CYCLES)) + { + pIn->cmdLast = TETRIS_INCMD_NONE; + } + + // reset repeat counter + pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY; + + // using cmdReturn as a flag for not leaving the loop + cmdReturn = TETRIS_INCMD_NONE; + break; + + default: + break; } // reset automatic falling if the player has dropped a piece @@ -418,12 +413,6 @@ tetris_input_command_t tetris_input_getCommand(tetris_input_t *pIn, } -/* Function: tetris_input_setLevel - * Description: modifies time interval of input events - * Argument pIn: pointer to an input object - * Argument nLvl: desired level (0 <= nLvl <= TETRIS_INPUT_LEVELS - 1) - * Return value: void - */ void tetris_input_setLevel(tetris_input_t *pIn, uint8_t nLvl) { @@ -439,11 +428,7 @@ void tetris_input_setLevel(tetris_input_t *pIn, } } -/* Function: tetris_input_resetDownKeyRepeat - * Description: resets the key repeat count for the down key - * Argument pIn: pointer to an input object - * Return value: void - */ + void tetris_input_resetDownKeyRepeat(tetris_input_t *pIn) { assert(pIn != NULL); @@ -452,3 +437,19 @@ void tetris_input_resetDownKeyRepeat(tetris_input_t *pIn) pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY; } } + + +void tetris_input_setOrientation(tetris_input_t *pIn, + tetris_orientation_t nOrient) +{ + if (pIn->nOrientation != nOrient) + { + pIn->nOrientation = nOrient; + + // avoid weird key repeating effects because the currently pressed + // button changes its meaning as soon as the orientation changes + pIn->cmdLast = tetris_input_mapCommand(pIn->nOrientation, + pIn->cmdRawLast); + pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY; + } +} diff --git a/games/tetris/input.h b/games/tetris/input.h index 4a62f49..66f0d93 100644 --- a/games/tetris/input.h +++ b/games/tetris/input.h @@ -2,101 +2,155 @@ #define INPUT_H_ #include +#include "orientation.h" +/** + * \defgroup TetrisInputDefinesPublic Input: Public constants + */ +/*@{*/ /*********** * defines * ***********/ -// number of levels +/** number of levels */ #define TETRIS_INPUT_LEVELS 30 +/*@}*/ + + +/** + * \defgroup TetrisInputTypes Input: Data types + */ +/*@{*/ /********* * types * *********/ +/** + * allowed input values + */ typedef enum tetris_input_command_t { - TETRIS_INCMD_ROT_CW, // rotate clockwise - TETRIS_INCMD_ROT_CCW, // rotate counter clockwise - TETRIS_INCMD_LEFT, // move piece left - TETRIS_INCMD_RIGHT, // move piece right - TETRIS_INCMD_DOWN, // lower piece by one row - TETRIS_INCMD_DROP, // move piece to the ground immediately - TETRIS_INCMD_GRAVITY, // piece gets pulled by gravity - TETRIS_INCMD_PAUSE, // pause the game - TETRIS_INCMD_NONE // idle (must alway be the last one) + TETRIS_INCMD_LEFT, /**< move piece left */ + TETRIS_INCMD_RIGHT, /**< move piece right */ + TETRIS_INCMD_DOWN, /**< lower piece by one row */ + TETRIS_INCMD_ROT_CW, /**< rotate clockwise */ + TETRIS_INCMD_ROT_CCW, /**< rotate counter clockwise */ + TETRIS_INCMD_DROP, /**< move piece to the ground immediately */ + TETRIS_INCMD_GRAVITY, /**< piece gets pulled by gravity */ + TETRIS_INCMD_PAUSE, /**< pause the game */ + TETRIS_INCMD_NONE /**< idle (must alway be the last one) */ } tetris_input_command_t; +/** + * values which influence the gravity time limit for a piece + */ typedef enum tetris_input_pace_t { - TETRIS_INPACE_HOVERING, // normal falling pace - TETRIS_INPACE_GLIDING /* guarantees a minimum docking time to avoid - accidentally docked pieces in higher levels */ + TETRIS_INPACE_HOVERING, /**< normal falling pace */ + TETRIS_INPACE_GLIDING /**< guarantees a minimum docking time to avoid + accidentally docked pieces in higher levels */ } tetris_input_pace_t; +/** + * data structure for the input module + */ typedef struct tetris_input_t { - // current level (determines falling speed) + /** + * current level (determines falling speed) + */ uint8_t nLevel; - // Amount of loop cycles between forced piece movements. This value gets - // set via the tetris_input_setLevel() function. + /** + * Amount of loop cycles between forced piece movements. This value gets + * set via the tetris_input_setLevel() function. + */ uint8_t nMaxCycles; - // This counter keeps track of the number of loop cycles which have been - // done since the last forced piece movement. It gets reset if it either - // reaches a well defined value (causing a gravity command to be issued) - // or the player has moved down the piece herself/himself. + /** + * This counter keeps track of the number of loop cycles which have been + * done since the last forced piece movement. It gets reset if it either + * reaches a well defined value (causing a gravity command to be issued) + * or the player has moved down the piece herself/himself. + */ uint8_t nLoopCycles; - // Amount of loop cycles in which the same command has been issued - // consecutively. It gets reset if either the current command differs from - // the last one or a well-defined value has been reached (thereby - // regulating the pace of the key repeat as commands are only processed - // if that value is reached). + /** + * Amount of loop cycles in which the same command has been issued + * consecutively. It gets reset if either the current command differs from + * the last one or a well-defined value has been reached (thereby + * regulating the pace of the key repeat as commands are only processed + * if that value is reached). + */ int8_t nRepeatCount; - // Keeps track of the number of loop cycles which have been run while in - // pause mode. As soon as a well defined value is reached, the game - // continues (in case someone paused the game and forgot to resume it). + /** + * Keeps track of the number of loop cycles which have been run while in + * pause mode. As soon as a well defined value is reached, the game + * continues (in case someone paused the game and forgot to resume it). + */ uint16_t nPauseCount; - // last command (important for key repeat) + /** + * last real command (important for key repeat and chatter protection) + */ + tetris_input_command_t cmdRawLast; + + + /** + * last mapped command (important for key repeat) + */ tetris_input_command_t cmdLast; - // Every command has its own counter. A command is ignored as long as its - // counter is unequal to 0. A counter gets set to a specific value (or 0) - // if the button of the corresponding command has been released by the - // player. All counters get decremented by one every loop cycle until they - // are zero. This is used to work against joystick chatter. Look at the - // TETRIS_INPUT_CHATTER_TICKS_... constants in input.c for the initial - // values of these counters. + + /** + * Every command has its own counter. A command is ignored as long as its + * counter is unequal to 0. A counter gets set to a specific value (or 0) + * if the button of the corresponding command has been released by the + * player. All counters get decremented by one every loop cycle until they + * are zero. This is used to work against joystick chatter. Look at the + * TETRIS_INPUT_CHATTER_TICKS_... constants in input.c for the initial + * values of these counters. + */ uint8_t nIgnoreCmdCounter[TETRIS_INCMD_NONE]; + + /** + * orientation of the direction mapping + */ + tetris_orientation_t nOrientation; } tetris_input_t; +/*@}*/ + + +/** + * \defgroup TetrisInputRelated Input: Interface functions + */ +/*@{*/ + /**************************** * construction/destruction * ****************************/ -/* Function: tetris_input_construct - * Description: constructs an input object for André's borg - * Return value: pointer to a newly created input object +/** + * constructs an input object for André's borg + * @return pointer to a newly created input object */ -tetris_input_t *tetris_input_construct(); +tetris_input_t *tetris_input_construct(void); -/* Function: tetris_input_destruct - * Description: destructs an input object - * Argument pIn: pointer to the input object which should be destructed - * Return value: void +/** + * destructs an input object + * @param pIn pointer to the input object which should be destructed */ void tetris_input_destruct(tetris_input_t *pIn); @@ -105,32 +159,41 @@ void tetris_input_destruct(tetris_input_t *pIn); * input related functions * ***************************/ -/* Function: retris_input_getCommand - * Description: retrieves commands from joystick or loop interval - * Argument pIn: pointer to an input object - * Argument nPace: falling pace (see definition of tetris_input_pace_t) - * Return value: see definition of tetris_input_command_t +/** + * retrieves commands from joystick or loop interval + * @param pIn pointer to an input object + * @param nPace falling pace + * @return see definition of tetris_input_command_t + * @see definition of tetris_input_pace_t */ tetris_input_command_t tetris_input_getCommand(tetris_input_t *pIn, - tetris_input_pace_t nPace, - uint8_t nFirstPerson); + tetris_input_pace_t nPace); -/* Function: tetris_input_setLevel - * Description: modifies time interval of input events - * Argument pIn: pointer to an input object - * Argument nLvl: desired level (0 <= nLvl <= TETRIS_INPUT_LEVELS - 1) - * Return value: void +/** + * modifies time interval of input events + * @param pIn pointer to an input object + * @param nLvl desired level (0 <= nLvl <= TETRIS_INPUT_LEVELS - 1) */ void tetris_input_setLevel(tetris_input_t *pIn, uint8_t nLvl); -/* Function: tetris_input_resetDownKeyRepeat - * Description: resets the key repeat count for the down key - * Argument pIn: pointer to an input object - * Return value: void +/** + * resets the key repeat count for the down key + * @param pIn pointer to an input object */ void tetris_input_resetDownKeyRepeat(tetris_input_t *pIn); + +/** + * set the orientation of the direction control mapping + * @param pIn pointer to an input object + * @param nOrient desired orientation + */ +void tetris_input_setOrientation(tetris_input_t *pIn, + tetris_orientation_t nOrient); + +/*@}*/ + #endif /*INPUT_H_*/ diff --git a/games/tetris/logic.c b/games/tetris/logic.c deleted file mode 100644 index 4fe3f41..0000000 --- a/games/tetris/logic.c +++ /dev/null @@ -1,721 +0,0 @@ -/* Borgtris - * by: Christian Kroll - * date: Tuesday, 2007/09/16 - */ - -#include -#include -#include -#include - -#include "../../autoconf.h" -#include "../../compat/eeprom.h" -#include "../../compat/pgmspace.h" -#include "../../menu/menu.h" -#include "../../random/prng.h" -#include "../../pixel.h" - -#include "logic.h" -#include "piece.h" -#include "playfield.h" -#include "view.h" -#include "input.h" -#include "highscore.h" - -#define NUMHIGHSCORES 3 - -#ifdef GAME_BASTET -#include "bast.h" -#endif - -#ifdef GAME_TETRIS_FP -#include "tetrisfp.h" -#endif - -#ifdef EEMEM -/*********************** - * Highscore in EEPROM * - ***********************/ - -uint16_t tetris_logic_nHighscore[NUMHIGHSCORES] EEMEM; -uint16_t tetris_logic_nHighscoreName[NUMHIGHSCORES] EEMEM; -#endif - -// Tetris icon, MSB is leftmost pixel -void tetris(); - -#ifdef MENU_SUPPORT -static uint8_t tetris_icon[8] PROGMEM = - { 0x0f, 0x0f, 0xc3, 0xdb, 0xdb, 0xc3, 0xf0, 0xf0 }; -game_descriptor_t tetris_game_descriptor - __attribute__((section(".game_descriptors"))) = -{ - &tetris, - tetris_icon, -}; - - -#ifdef GAME_TETRIS_FP -// Bastet icon, MSB is leftmost pixel -static uint8_t tetrisfp_icon[8] PROGMEM = - { 0xee, 0x89, 0xee, 0x88, 0x88, 0x20, 0x2c, 0x6c }; -game_descriptor_t tetrisfp_game_descriptor - __attribute__((section(".game_descriptors"))) = -{ - &tetris_fp, - tetrisfp_icon, -}; -#endif - -#ifdef GAME_BASTET -// Bastet icon, MSB is leftmost pixel -static uint8_t bastet_icon[8] PROGMEM = - { 0x81, 0xc3, 0xff, 0x99, 0xff, 0xff, 0x66, 0x3c }; -game_descriptor_t bastet_game_descriptor - __attribute__((section(".game_descriptors"))) = -{ - &tetris_bastet, - bastet_icon, -}; -#endif - - -#endif /*MENU_SUPPORT*/ - -/*************************** - * non-interface functions * - ***************************/ - -/* Function: tetris_logic_calculateLines - * Description: calculates number of lines for the given row mask - * Argument nRowMask: row mask from which the no. of lines will be calculated - * Return value: number of lines of the row mask - */ -uint8_t tetris_logic_calculateLines(uint8_t nRowMask) -{ - uint8_t nMask = 0x0001; - uint8_t nLines = 0; - for (uint8_t i = 0; i < 4; ++i) - { - if ((nMask & nRowMask) != 0) - { - ++nLines; - } - nMask <<= 1; - } - - return nLines; -} - - -/* Function: tetris_logic_retrieveHighscore - * Description: retrieves the highscore from storate - * Argument nHighscoreIndex: highscore index (for different game variants) - * Return value: the highscore - */ -uint16_t tetris_logic_retrieveHighscore(uint8_t nHighscoreIndex) -{ -#ifdef EEMEM - uint16_t nHighscore = 0; - nHighscore = eeprom_read_word(&tetris_logic_nHighscore[nHighscoreIndex]); - - // a score of 65535 is most likely caused by uninitialized EEPROM addresses - if (nHighscore == 65535) - { - nHighscore = 0; - } - - return nHighscore; -#else - return 0; -#endif -} - - -/* Function: tetris_logic_saveHighscore - * Description: saves the highscore into the storage - * Argument nHighscoreIndex: highscore index (for different game variants) - * Argument nHighscoreName: the highscore - * Return value: void - */ -void tetris_logic_saveHighscore(uint8_t nHighscoreIndex, uint16_t nHighscore) -{ -#ifdef EEMEM - if (nHighscore > tetris_logic_retrieveHighscore(nHighscoreIndex)) - { - eeprom_write_word(&tetris_logic_nHighscore[nHighscoreIndex], - nHighscore); - } -#endif -} - - -/* Function: tetris_logic_retrieveHighscoreName - * Description: retrieves the initials of the champion from storage - * Argument nHighscoreIndex: highscore index (for different game variants) - * Return value: the initials of the champion packed into a uint16_t - */ -uint16_t tetris_logic_retrieveHighscoreName(uint8_t nHighscoreIndex) -{ -#ifdef EEMEM - uint16_t nHighscoreName = 0; - nHighscoreName = - eeprom_read_word(&tetris_logic_nHighscoreName[nHighscoreIndex]); - - // a score of 65535 is most likely caused by uninitialized EEPROM addresses - if (nHighscoreName == 65535) - { - nHighscoreName = 0; - } - - return nHighscoreName; -#else - return 0; -#endif -} - - -/* Function: tetris_logic_saveHighscoreName - * Description: saves the initials of the champion - * Argument nHighscoreIndex: highscore index (for different game variants) - * Argument nHighscoreName: the initials of the champion packed into a uint16_t - * Return value: void - */ -void tetris_logic_saveHighscoreName(uint8_t nHighscoreIndex, - uint16_t nHighscoreName) -{ -#ifdef EEMEM - eeprom_write_word(&tetris_logic_nHighscoreName[nHighscoreIndex], - nHighscoreName); -#endif -} - - -/**************************** - * construction/destruction * - ****************************/ - -/* Function: tetris_logic_construct - * Description: constructs a logic object - * Argument nBastet: 0 for normal tetris, 1 for bastet - * Return value: pointer to a newly created logic object - */ -tetris_logic_t *tetris_logic_construct(uint8_t nBastet) -{ - tetris_logic_t *pLogic = (tetris_logic_t *) malloc(sizeof(tetris_logic_t)); - assert(pLogic != NULL); - memset(pLogic, 0, sizeof(tetris_logic_t)); - pLogic->nBastet = nBastet; - return pLogic; -} - -/* Function: tetris_logic_destruct - * Description: destructs a logic object - * Argument pIn: pointer to the logic object to be destructed - * Return value: void - */ -void tetris_logic_destruct(tetris_logic_t *pLogic) -{ - assert(pLogic != 0); - free(pLogic); -} - - -/*************************** - * logic related functions * - ***************************/ - -/* Function: tetris - * Description: runs the tetris game - * Return value: void - */ -void tetris() -{ - tetris_main(0); -} - - -/* Function: tetris_bastet - * Description: runs the bastet game - * Return value: void - */ -void tetris_bastet() -{ - tetris_main(1); -} - - -/* Function: tetris_fp - * Description: runs the tetris first person game - * Return value: void - */ -void tetris_fp() -{ - tetris_main(2); -} - - -/* Function: tetris_main - * Description: runs the tetris game - * Argument nMode: 0 for normal Tetris, 1 for Bastet - * 2 for first person tetris - * Return value: void - */ -void tetris_main(int8_t nMode) -{ - // get view dependent dimensions of the playfield - int8_t nWidth; - int8_t nHeight; - tetris_view_getDimensions(&nWidth, &nHeight); - - // holds the current user command which should be processed - tetris_input_command_t inCmd; - -#ifdef GAME_TETRIS_FP - tetris_screendir = 0; -#endif - - // prepare data structures that drive the game... - tetris_logic_t *pLogic = tetris_logic_construct((nMode==1)); - tetris_playfield_t *pPl = tetris_playfield_construct(nWidth, nHeight); - tetris_input_t *pIn = tetris_input_construct(); - tetris_view_t *pView = tetris_view_construct(pLogic, pPl, (nMode==2)); -#ifdef GAME_BASTET - tetris_bastet_t *pBastet; -#endif - - // runtime variable - int8_t nPieceRow; - - // retrieve highscore - static uint16_t nHighscore = 0; - static uint16_t nHighscoreName = 0; -#ifndef GAME_BASTET - if (nHighscore == 0) - { -#endif - nHighscore = tetris_logic_retrieveHighscore(nMode); - nHighscoreName = tetris_logic_retrieveHighscoreName(nMode); -#ifndef GAME_BASTET - } -#endif - - // initialize current and next piece - tetris_piece_t *pPiece; - tetris_piece_t *pNextPiece; -#ifdef GAME_BASTET - if (nMode == 1) - { - pBastet = tetris_bastet_construct(pPl); - pNextPiece = pPiece = NULL; - } - else - { -#endif - pNextPiece = pPiece = - tetris_piece_construct(random8() % 7, TETRIS_PC_ANGLE_0); - tetris_logic_setPreviewPiece(pLogic, pNextPiece); -#ifdef GAME_BASTET - } -#endif - - // the view only monitors the logic and the playfield object for the game - // status so we must put information like the next piece or the current - // highscore to a place where the view can find it - tetris_logic_setHighscore(pLogic, nHighscore); - tetris_logic_setHighscoreName(pLogic, nHighscoreName); - - // pace flag - tetris_input_pace_t inPace; - - // game loop, runs as long as the game is not over - while (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER) - { - // what we do strongly depends on the status of the playfield - switch (tetris_playfield_getStatus(pPl)) - { - // the playfield awaits a new piece - case TETRIS_PFS_READY: -#ifdef GAME_BASTET - if (nMode == 1) - { - if (pPiece != NULL) - { - tetris_piece_destruct(pPiece); - } - if (pNextPiece != NULL) - { - tetris_piece_destruct(pNextPiece); - } - pPiece = tetris_bastet_choosePiece(pBastet); - pNextPiece = tetris_bastet_choosePreviewPiece(pBastet); - tetris_piece_t *pOldPiece; - tetris_playfield_insertPiece(pPl, pPiece, &pOldPiece); - tetris_logic_setPreviewPiece(pLogic, pNextPiece); - } - else - { -#endif - // make preview piece the current piece and create a new - // preview piece - pPiece = pNextPiece; - pNextPiece = tetris_piece_construct(random8() % 7, - TETRIS_PC_ANGLE_0); - tetris_logic_setPreviewPiece(pLogic, pNextPiece); - - // insert new piece into playfield - tetris_piece_t *pOldPiece; - tetris_playfield_insertPiece(pPl, pPiece, &pOldPiece); - - // destruct old piece (if it exists) since we don't need it - // anymore - if (pOldPiece != NULL) - { - tetris_piece_destruct(pOldPiece); - pOldPiece = NULL; - } -#ifdef GAME_BASTET - } -#endif - break; - - // a piece is hovering and can be controlled by the player - case TETRIS_PFS_HOVERING: - case TETRIS_PFS_GLIDING: - // if the piece is gliding the input module has to grant us - // a minimum amount of time to move it - if (tetris_playfield_getStatus(pPl) == TETRIS_PFS_GLIDING) - { - inPace = TETRIS_INPACE_GLIDING; - } - else - { - inPace = TETRIS_INPACE_HOVERING; - } - - // ensure correct view mode if the game isn't paused - if ((inCmd = tetris_input_getCommand(pIn, inPace, (nMode==2))) - != TETRIS_INCMD_PAUSE) - { - tetris_view_setViewMode(pView, TETRIS_VIMO_RUNNING); - } - - // what we do depends on what the input module tells us - switch (inCmd) - { - // game paused? - case TETRIS_INCMD_PAUSE: - // tell the view it should display the pause screen - tetris_view_setViewMode(pView, TETRIS_VIMO_PAUSED); - break; - - // the piece was pulled down by the almighty gravity - case TETRIS_INCMD_GRAVITY: - tetris_playfield_advancePiece(pPl); - break; - - // the player has pulled down the piece herself/himself - case TETRIS_INCMD_DOWN: - tetris_playfield_advancePiece(pPl); - // if the game still runs, reward the player with extra points - if (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER) - { - tetris_logic_singleDrop(pLogic, 1); - } - break; - - // player shifted the piece to the left - case TETRIS_INCMD_LEFT: - tetris_playfield_movePiece(pPl, TETRIS_PFD_LEFT); - break; - - // player shifted the piece to the right - case TETRIS_INCMD_RIGHT: - tetris_playfield_movePiece(pPl, TETRIS_PFD_RIGHT); - break; - - // player rotated the piece clockwise - case TETRIS_INCMD_ROT_CW: -#ifdef GAME_TETRIS_FP - if (nMode == 2) { - tetris_view_rotate(); - tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CCW); - } else - { -#endif - tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CW); -#ifdef GAME_TETRIS_FP - } -#endif - break; - - // player rotated the piece counter clockwise - case TETRIS_INCMD_ROT_CCW: - tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CCW); - break; - - // the player decided to make an immediate drop - case TETRIS_INCMD_DROP: - nPieceRow = tetris_playfield_getRow(pPl); - // emulate immediate drop - while((tetris_playfield_getStatus(pPl) == TETRIS_PFS_GLIDING) || - (tetris_playfield_getStatus(pPl) == TETRIS_PFS_HOVERING)) - { - tetris_playfield_advancePiece(pPl); - } - // if the game still runs, reward the player with extra points - if (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER) - { - tetris_logic_completeDrop(pLogic, - tetris_playfield_getRow(pPl) - nPieceRow); - } - break; - - // avoid compiler warnings - default: - break; - } - break; - - // the piece has irrevocably hit the ground - case TETRIS_PFS_DOCKED: - // avoid accidentally issued "down" commands - tetris_input_resetDownKeyRepeat(pIn); - - // remove complete lines (if any) - tetris_playfield_removeCompleteLines(pPl); - - // let the logic object decide how many points the player gets - // and whether the level gets changed - tetris_logic_removedLines(pLogic, tetris_playfield_getRowMask(pPl)); - tetris_input_setLevel(pIn, tetris_logic_getLevel(pLogic)); - break; - - // avoid compiler warnings - default: - break; - } - - // the view updates it state every loop cycle to make changes visible - tetris_view_update(pView); - } - - // game is over and we provide the player with her/his results - tetris_view_showResults(pView); - - // update highscore if it has been beaten - uint16_t nScore = tetris_logic_getScore(pLogic); - if (nScore > nHighscore) - { - nHighscore = nScore; - nHighscoreName = tetris_highscore_inputName(); - tetris_logic_saveHighscore(nMode, nHighscore); - tetris_logic_saveHighscoreName(nMode, nHighscoreName); - } - - // clean up -#ifdef GAME_BASTET - if (nMode == 1) - { - tetris_bastet_destruct(pBastet); - } -#endif - tetris_view_destruct(pView); - tetris_input_destruct(pIn); - tetris_playfield_destruct(pPl); - tetris_logic_destruct(pLogic); - tetris_piece_destruct(pPiece); - tetris_piece_destruct(pNextPiece); -} - - -/* Function: tetris_logic_singleDrop - * Description: add points which result from single step dropping - * Argument pLogic: the logic object we want to modify - * Argument nLines: the number of rows involved - * Return value: void - */ -void tetris_logic_singleDrop(tetris_logic_t *pLogic, - uint8_t nLines) -{ - assert(pLogic != 0); -#ifdef GAME_BASTET - if (pLogic->nBastet) return; -#endif - pLogic->nScore += nLines; -} - - -/* Function: tetris_logic_completeDrop - * Description: add points which result from a complete drop - * Argument pLogic: the logic object we want to modify - * Argument nLines: the number of rows involved - * Return value: void - */ -void tetris_logic_completeDrop(tetris_logic_t *pLogic, - uint8_t nLines) -{ - assert(pLogic != 0); -#ifdef GAME_BASTET - if (pLogic->nBastet) return; -#endif - pLogic->nScore += nLines * 2; -} - - -/* Function: tetris_logic_removedLines - * Description: add points which result from removed rows - * Argument pLogic: the logic object we want to modify - * Argument nRowMask: see tetris_playfield_completeLines - * Return value: void - */ -void tetris_logic_removedLines(tetris_logic_t *pLogic, - uint8_t nRowMask) -{ - assert(pLogic != 0); - uint8_t nLines = tetris_logic_calculateLines(nRowMask); - pLogic->nLines += nLines; - pLogic->nLevel = ((pLogic->nLines / 10) < TETRIS_INPUT_LEVELS) ? - (pLogic->nLines / 10) : (TETRIS_INPUT_LEVELS - 1); - -#ifdef GAME_BASTET - if (pLogic->nBastet) - { - pLogic->nScore += nLines; - return; - } -#endif - switch (nLines) - { - case 1: - pLogic->nScore += 50; - break; - case 2: - pLogic->nScore += 150; - break; - case 3: - pLogic->nScore += 250; - break; - case 4: - pLogic->nScore += 400; - break; - } -} - - -/***************** - * get functions * - *****************/ - -/* Function: tetris_logic_getScore - * Description: returns the current score - * Argument pLogic: the logic object we want information from - * Return value: the score as uint16_t - */ -uint16_t tetris_logic_getScore(tetris_logic_t *pLogic) -{ - assert(pLogic != NULL); - return pLogic->nScore; -} - - -/* Function: tetris_logic_getHighscore - * Description: returns the current highscore - * Argument pLogic: the logic object we want information from - * Return value: the highscore as uint16_t - */ - -uint16_t tetris_logic_getHighscore(tetris_logic_t *pLogic) -{ - assert(pLogic != NULL); - return pLogic->nHighscore; -} - - -/* Function: tetris_logic_setHighscore - * Description: set highscore - * Argument pLogic: the logic object we want to modify - * Argmument nHighscore: highscore - */ -void tetris_logic_setHighscore(tetris_logic_t *pLogic, - uint16_t nHighscore) -{ - assert(pLogic != NULL); - pLogic->nHighscore = nHighscore; -} - - -/* Function: tetris_logic_getHighscoreName - * Description: returns the current highscore name - * Argument pLogic: the logic object we want information from - * Return value: the highscore name packed as uint16_t - */ -uint16_t tetris_logic_getHighscoreName(tetris_logic_t *pLogic) -{ - assert(pLogic != NULL); - return pLogic->nHighscoreName; -} - - - -/* Function: tetris_logic_setHighscoreName - * Description: set highscore name - * Argument pLogic: the logic object we want to modify - * Argmument nHighscoreName: highscore name - */ -void tetris_logic_setHighscoreName(tetris_logic_t *pLogic, - uint16_t nHighscoreName) -{ - assert(pLogic != NULL); - pLogic->nHighscoreName = nHighscoreName; -} - - -/* Function: tetris_logic_getLevel - * Description: returns the current level - * Argument pLogic: the logic object we want information from - * Return value: the level as uint8_t - */ -uint8_t tetris_logic_getLevel(tetris_logic_t *pLogic) -{ - assert(pLogic != NULL); - return pLogic->nLevel; -} - - -/* Function: tetris_logic_getLines - * Description: returns the number of completed lines - * Argument pLogic: the logic object we want information from - * Return value: number of completed lines as uint16_t - */ -uint16_t tetris_logic_getLines(tetris_logic_t *pLogic) -{ - assert(pLogic != NULL); - return pLogic->nLines; -} - - -/* Function: tetris_logic_setPreviewPiece - * Description: help for the view to determine the preview piece - * Argument pLogic: the logic object we want to modify - * Argument pPiece: pointer to piece intended to be the next one (may be NULL) - * Return value: void - */ -void tetris_logic_setPreviewPiece(tetris_logic_t *pLogic, - tetris_piece_t *pPiece) -{ - assert(pLogic != NULL); - pLogic->pPreviewPiece = pPiece; -} - - -/* Function: tetris_logic_getPreviewPiece - * Description: returns piece which was set via tetris_logic_setPreviewPiece - * Argument pLogic: the logic object we want information from - * Return value: the piece intended to be the next one (may be NULL) - */ -tetris_piece_t* tetris_logic_getPreviewPiece(tetris_logic_t *pLogic) -{ - assert(pLogic != NULL); - return pLogic->pPreviewPiece; -} diff --git a/games/tetris/logic.h b/games/tetris/logic.h deleted file mode 100644 index 874b17d..0000000 --- a/games/tetris/logic.h +++ /dev/null @@ -1,190 +0,0 @@ -#ifndef TETRIS_LOGIC_H_ -#define TETRIS_LOGIC_H_ - -#include -#include "piece.h" - -/********* - * types * - *********/ - -typedef struct tetris_logic_t -{ - uint8_t nBastet; // is gametype bastet? - uint16_t nScore; // score of the player - uint16_t nHighscore; // highscore - uint16_t nHighscoreName; // name of the person who achieved highscore - uint8_t nLevel; // current level - uint16_t nLines; // number of completed lines - tetris_piece_t *pPreviewPiece; // the piece intended to be the next one -} -tetris_logic_t; - -/**************************** - * construction/destruction * - ****************************/ - -/* Function: tetris_logic_construct - * Description: constructs a logic object - * Argument nBastet: 0 for normal tetris, 1 for bastet - * Return value: pointer to a newly created logic object - */ -tetris_logic_t *tetris_logic_construct(uint8_t nBastet); - -/* Function: tetris_logic_destruct - * Description: destructs a logic object - * Argument pIn: pointer to the logic object to be destructed - * Return value: void - */ -void tetris_logic_destruct(tetris_logic_t *pLogic); - -/*************************** - * logic related functions * - ***************************/ - -/* Function: tetris - * Description: runs the tetris game - * Return value: void - */ -void tetris(); - - -/* Function: tetris_bastet - * Description: runs the bastet game - * Return value: void - */ -void tetris_bastet(); - -/* Function: tetris_fp - * Description: runs the tetris first person game - * Return value: void - */ -void tetris_fp(); - - -/* Function: tetris_main - * Description: runs the tetris game - * Argument nMode: 0 for normal Tetris, 1 for Bastet, - * 2 for first person tetris - * Return value: void - */ -void tetris_main(int8_t nMode); - -/* Function: tetris_logic_singleDrop - * Description: add points which result from single step dropping - * Argument pLogic: the logic object we want to modify - * Argument nLines: the number of rows involved - * Return value: void - */ -void tetris_logic_singleDrop(tetris_logic_t *pLogic, - uint8_t nLines); - - -/* Function: tetris_logic_completeDrop - * Description: add points which result from a complete drop - * Argument pLogic: the logic object we want to modify - * Argument nLines: the number of rows involved - * Return value: void - */ -void tetris_logic_completeDrop(tetris_logic_t *pLogic, - uint8_t nLines); - - -/* Function: tetris_logic_removedLines - * Description: add points which result from removed rows - * Argument pLogic: the logic object we want to modify - * Argument nRowMask: see tetris_playfield_completeLines - * Return value: void - */ -void tetris_logic_removedLines(tetris_logic_t *pLogic, - uint8_t nRowMask); - - -/********************* - * get/set functions * - *********************/ - -/* Function: tetris_logic_getScore - * Description: returns the current score - * Argument pLogic: the logic object we want information from - * Return value: the score as uint16_t - */ -uint16_t tetris_logic_getScore(tetris_logic_t *pLogic); - - -/* Function: tetris_logic_getHighscore - * Description: returns the current highscore - * Argument pLogic: the logic object we want information from - * Return value: the highscore as uint16_t - */ -uint16_t tetris_logic_getHighscore(tetris_logic_t *pLogic); - - -/* Function: tetris_logic_setHighscore - * Description: set highscore - * Argument pLogic: the logic object we want to modify - * Argmument nHighscore: highscore - */ -void tetris_logic_setHighscore(tetris_logic_t *pLogic, - uint16_t nHighscore); - - -/* Function: tetris_logic_getHighscoreName - * Description: returns the current highscore name - * Argument pLogic: the logic object we want information from - * Return value: the highscore name packed as uint16_t - */ -uint16_t tetris_logic_getHighscoreName(tetris_logic_t *pLogic); - - -/* Function: tetris_logic_setHighscoreName - * Description: set highscore name - * Argument pLogic: the logic object we want to modify - * Argmument nHighscoreName: highscore name - */ -void tetris_logic_setHighscoreName(tetris_logic_t *pLogic, - uint16_t nHighscoreName); - - -/* Function: tetris_logic_getLevel - * Description: returns the current level - * Argument pLogic: the logic object we want information from - * Return value: the level as uint8_t - */ -uint8_t tetris_logic_getLevel(tetris_logic_t *pLogic); - - -/* Function: tetris_logic_getLines - * Description: returns the number of completed lines - * Argument pLogic: the logic object we want information from - * Return value: number of completed lines as uint16_t - */ -uint16_t tetris_logic_getLines(tetris_logic_t *pLogic); - - -/* Function: tetris_logic_setPreviewPiece - * Description: help for the view to determine the preview piece - * Argument pLogic: the logic object we want to modify - * Argument pPiece: pointer to piece intended to be the next one - * Return value: void - */ -void tetris_logic_setPreviewPiece(tetris_logic_t *pLogic, - tetris_piece_t *pPiece); - - -/* Function: tetris_logic_getPreviewPiece - * Description: returns piece which was set via tetris_logic_setPreviewPiece - * Argument pLogic: the logic object we want information from - * Return value: the piece intended to be the next one - */ -tetris_piece_t* tetris_logic_getPreviewPiece(tetris_logic_t *pLogic); - -#ifdef GAME_TETRIS_FP -/* Function: tetris_view_rotate - * Description: rotate view for first person mode - * Return value: void - */ -void tetris_view_rotate(void); -#endif - -#endif /*TETRIS_LOGIC_H_*/ diff --git a/games/tetris/orientation.h b/games/tetris/orientation.h new file mode 100644 index 0000000..044f6cc --- /dev/null +++ b/games/tetris/orientation.h @@ -0,0 +1,13 @@ +#ifndef ORIENTATION_H_ +#define ORIENTATION_H_ + +typedef enum tetris_orientation_t +{ + TETRIS_ORIENTATION_0, + TETRIS_ORIENTATION_90, + TETRIS_ORIENTATION_180, + TETRIS_ORIENTATION_270 +} +tetris_orientation_t; + +#endif /* ORIENTATION_H_ */ diff --git a/games/tetris/piece.c b/games/tetris/piece.c index 6b2e460..9eae4ba 100644 --- a/games/tetris/piece.c +++ b/games/tetris/piece.c @@ -14,12 +14,6 @@ * construction/destruction * *****************************/ -/* Function: tetris_piece_construct - * Description: constructs a piece with the given attributes - * Argument s: shape of the piece (see tetris_piece_shape_t) - * Argument a: its angle (see tetris_piece_angel_t) - * Return value: pointer to a newly created piece - */ tetris_piece_t *tetris_piece_construct(tetris_piece_shape_t s, tetris_piece_angle_t a) { @@ -32,11 +26,6 @@ tetris_piece_t *tetris_piece_construct(tetris_piece_shape_t s, return p_piece; } -/* Function: tetris_piece_destruct - * Description: destructs a piece - * Argument pPc: pointer to the piece to be destructed - * Return value: void - */ void tetris_piece_destruct(tetris_piece_t *pPc) { assert(pPc != NULL); @@ -48,13 +37,6 @@ void tetris_piece_destruct(tetris_piece_t *pPc) * piece related functions * ****************************/ -/* Function: tetris_piece_getBitmap - * Description: returns bitfield representation of the piece - * Argument pPc: piece from which the bitfield shuld be retrieved - * Return value: bitfield representation of the piece - * - nth nibble is nth row of the piece (from upper left) - * - the LSB of a nibble represents the left side of a row - */ uint16_t tetris_piece_getBitmap(tetris_piece_t *pPc) { assert(pPc != NULL); @@ -79,12 +61,6 @@ uint16_t tetris_piece_getBitmap(tetris_piece_t *pPc) } -/* Function: tetris_piece_rotate - * Description: rotates a piece - * Argument pPc: piece to rotate - * Argument r: type of rotation (see tetris_piece_rotation_t) - * Return value: void - */ void tetris_piece_rotate(tetris_piece_t *pPc, tetris_piece_rotation_t r) { @@ -117,14 +93,9 @@ void tetris_piece_rotate(tetris_piece_t *pPc, } } -/* Function: tetris_piece_changeShape - * Description: changes the shape of a piece - * Argument pPc: piece to change - * Argument shape: the shape of interest - * Return value: void - */ -void tetris_piece_changeShape(tetris_piece_t *pPc, - tetris_piece_shape_t shape) + +void tetris_piece_setShape(tetris_piece_t *pPc, + tetris_piece_shape_t shape) { assert(pPc != NULL); assert((shape >= 0) && (shape <= TETRIS_PC_Z)); @@ -133,14 +104,8 @@ void tetris_piece_changeShape(tetris_piece_t *pPc, } -/* Function: tetris_piece_changeAngle - * Description: changes the angle of a piece - * Argument pPc: piece to change - * Argument angle: the angle of interest - * Return value: void - */ -void tetris_piece_changeAngle(tetris_piece_t *pPc, - tetris_piece_angle_t angle) +void tetris_piece_setAngle(tetris_piece_t *pPc, + tetris_piece_angle_t angle) { assert(pPc != NULL); assert((angle >= TETRIS_PC_ANGLE_0) && (angle <= TETRIS_PC_ANGLE_270)); @@ -149,12 +114,7 @@ void tetris_piece_changeAngle(tetris_piece_t *pPc, } -/* Function: tetris_piece_angleCount - * Description: returns the number of different angles - * Argument pPc: piece whose angle count is of interest - * Return value: number of different angles - */ -int8_t tetris_piece_angleCount(tetris_piece_t *pPc) +int8_t tetris_piece_getAngleCount(tetris_piece_t *pPc) { assert(pPc != NULL); diff --git a/games/tetris/piece.h b/games/tetris/piece.h index 6ebbd2c..f7283ba 100644 --- a/games/tetris/piece.h +++ b/games/tetris/piece.h @@ -3,122 +3,135 @@ #include +/** + * \defgroup TetrisPieceTypes Piece: Data types + */ +/*@{*/ /********* * types * *********/ +/** shape attributes for a piece */ typedef enum tetris_piece_shape_t { - TETRIS_PC_LINE, - TETRIS_PC_T, - TETRIS_PC_SQUARE, - TETRIS_PC_L, - TETRIS_PC_LBACK, - TETRIS_PC_S, - TETRIS_PC_Z + TETRIS_PC_LINE, /**< the I shaped brick */ + TETRIS_PC_T, /**< the T shaped brick */ + TETRIS_PC_SQUARE, /**< the sqare shaped brick */ + TETRIS_PC_L, /**< the L shaped brick */ + TETRIS_PC_LBACK, /**< the reverse L shaped brick */ + TETRIS_PC_S, /**< the S shaped brick */ + TETRIS_PC_Z /**< the Z shaped brick */ } tetris_piece_shape_t; +/** possible angles for a brick */ typedef enum tetris_piece_angle_t { - TETRIS_PC_ANGLE_0, - TETRIS_PC_ANGLE_90, - TETRIS_PC_ANGLE_180, - TETRIS_PC_ANGLE_270 + TETRIS_PC_ANGLE_0, /**< standard angle */ + TETRIS_PC_ANGLE_90, /**< rotated by 90 degrees */ + TETRIS_PC_ANGLE_180, /**< rotated by 180 degrees */ + TETRIS_PC_ANGLE_270 /**< rotated by 270 degrees */ } tetris_piece_angle_t; +/** rotation attributes */ typedef enum tetris_piece_rotation_t { - TETRIS_PC_ROT_CW, // clockwise rotation - TETRIS_PC_ROT_CCW // counter clockwise rotation + TETRIS_PC_ROT_CW, /**< clockwise rotation */ + TETRIS_PC_ROT_CCW /**< counter clockwise rotation */ } tetris_piece_rotation_t; - +/** + * describes the attributes of a piece + * @see tetris_piece_shape_t + * @see tetris_piece_angle_t + */ typedef struct tetris_piece_t { - tetris_piece_shape_t shape; // specifies the shape of the piece - tetris_piece_angle_t angle; // specifies one of 4 angels + tetris_piece_shape_t shape; /**< specifies the shape of the piece */ + tetris_piece_angle_t angle; /**< specifies one of 4 angels */ } tetris_piece_t; +/*@}*/ + + +/** + * \defgroup TetrisPieceRelated Piece: Interface functions + */ +/*@{*/ /***************************** * construction/destruction * *****************************/ -/* Function: tetris_piece_construct - * Description: constructs a piece with the given attributes - * Argument s: shape of the piece (see tetris_piece_shape_t) - * Argument a: its angle (see tetris_piece_angel_t) - * Return value: pointer to a newly created piece +/** + * constructs a piece with the given attributes + * @param s shape of the piece (see tetris_piece_shape_t) + * @param a its angle (see tetris_piece_angel_t) + * @return pointer to a newly created piece */ tetris_piece_t *tetris_piece_construct(tetris_piece_shape_t s, tetris_piece_angle_t a); -/* Function: tetris_piece_destruct - * Description: destructs a piece - * Argument pPc: pointer to the piece to be destructed - * Return value: void +/** + * destructs a piece + * @param pPc pointer to the piece to be destructed */ void tetris_piece_destruct(tetris_piece_t *pPc); -/**************************** - * piece related functions * - ****************************/ +/*************************** + * piece related functions * + ***************************/ -/* Function: tetris_piece_getBitmap - * Description: returns bitfield representation of the piece - * Argument pPc: piece from which the bitfield shuld be retrieved - * Return value: bitfield representation of the piece - * - nth nibble is nth row of the piece (from upper left) - * - the LSB of a nibble represents the left side of a row +/** + * returns bitfield representation of the piece + * @param pPc piece from which the bitfield shuld be retrieved + * @return bitfield representation of the piece */ uint16_t tetris_piece_getBitmap(tetris_piece_t *pPc); -/* Function: tetris_piece_rotate - * Description: rotates a piece - * Argument pPc: piece to rotate - * Argument r: type of rotation (see tetris_piece_rotation_t) - * Return value: void +/** + * rotates a piece + * @param pPc piece to rotate + * @param r type of rotation (see tetris_piece_rotation_t) */ void tetris_piece_rotate(tetris_piece_t *pPc, tetris_piece_rotation_t r); -/* Function: tetris_piece_changeShape - * Description: changes the shape of a piece - * Argument pPc: piece to change - * Argument shape: the shape of interest - * Return value: void +/** + * changes the shape of a piece + * @param pPc piece to change + * @param shape the shape of interest */ -void tetris_piece_changeShape(tetris_piece_t *pPc, - tetris_piece_shape_t shape); +void tetris_piece_setShape(tetris_piece_t *pPc, + tetris_piece_shape_t shape); -/* Function: tetris_piece_changeAngle - * Description: changes the angle of a piece - * Argument pPc: piece to change - * Argument angle: the angle of interest - * Return value: void +/** + * changes the angle of a piece + * @param pPc piece to change + * @param angle the angle of interest */ -void tetris_piece_changeAngle(tetris_piece_t *pPc, - tetris_piece_angle_t angle); +void tetris_piece_setAngle(tetris_piece_t *pPc, + tetris_piece_angle_t angle); -/* Function: tetris_piece_angleCount - * Description: returns the number of different angles - * Argument pPc: piece whose angle count is of interest - * Return value: number of different angles +/** + * returns the number of different angles + * @param pPc piece whose angle count we want to know + * @return number of different angles */ -int8_t tetris_piece_angleCount(tetris_piece_t *pPc); +int8_t tetris_piece_getAngleCount(tetris_piece_t *pPc); +/*@}*/ #endif /*TETRIS_PIECE_H_*/ diff --git a/games/tetris/playfield.c b/games/tetris/playfield.c index 66516e9..7ff258d 100644 --- a/games/tetris/playfield.c +++ b/games/tetris/playfield.c @@ -11,10 +11,10 @@ * non-interface functions * ***************************/ -/* Function: tetris_playfield_hoverStatus; - * Description: determines if piece is either hovering or gliding - * Argument pPl: the playfield we want information from - * Return value: TETRIS_PFS_HOVERING or TETRIS_PFS_GLIDING +/** + * determines if piece is either hovering or gliding + * @param pPl the playfield we want information from + * @eturn TETRIS_PFS_HOVERING or TETRIS_PFS_GLIDING */ tetris_playfield_status_t tetris_playfield_hoverStatus(tetris_playfield_t* pPl) { @@ -35,12 +35,6 @@ tetris_playfield_status_t tetris_playfield_hoverStatus(tetris_playfield_t* pPl) * construction/destruction * ****************************/ -/* Function: tetris_playfield_construct - * Description: constructs a playfield with the given dimensions - * Argument nWidth: width of playfield (4 <= n <= 16) - * Argument nHeight: height of playfield (4 <= n <= 124) - * Return value: pointer to a newly created playfield - */ tetris_playfield_t *tetris_playfield_construct(int8_t nWidth, int8_t nHeight) { @@ -75,11 +69,6 @@ tetris_playfield_t *tetris_playfield_construct(int8_t nWidth, } -/* Function: tetris_playfield_destruct - * Description: destructs a playfield - * Argument pPl: pointer to the playfield to be destructed - * Return value: void - */ void tetris_playfield_destruct(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -97,11 +86,23 @@ void tetris_playfield_destruct(tetris_playfield_t *pPl) * playfield related functions * *******************************/ -/* Function: tetris_playfield_reset - * Description: resets playfield to begin a new game - * Argument pPl: playfield to perform action on - * Return value: void - */ +uint8_t tetris_playfield_calculateLines(uint8_t nRowMask) +{ + uint8_t nMask = 0x0001; + uint8_t nLines = 0; + for (uint8_t i = 0; i < 4; ++i) + { + if ((nMask & nRowMask) != 0) + { + ++nLines; + } + nMask <<= 1; + } + + return nLines; +} + + void tetris_playfield_reset(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -141,13 +142,6 @@ int8_t tetris_playfield_getPieceStartPos(tetris_piece_t *pPiece) } -/* Function: tetris_playfield_insertPiece - * Description: inserts a new piece - * Argument pPl: playfield to perform action on - * Argument pPiece: piece to be inserted - * Argument ppOldPiece: [out] indirect pointer to former piece for deallocation - * Return value: void - */ void tetris_playfield_insertPiece(tetris_playfield_t *pPl, tetris_piece_t *pPiece, tetris_piece_t** ppOldPiece) @@ -184,13 +178,6 @@ void tetris_playfield_insertPiece(tetris_playfield_t *pPl, } -/* Function: tetris_playfield_collision - * Description: detects if piece collides with s.th. at a given position - * Argument pPl: playfield to perform action on - * Argument nColumn: column where the piece should be moved - * Argument nRow: row where the piece should be moved - * Return value: 1 for collision, 0 otherwise - */ uint8_t tetris_playfield_collision(tetris_playfield_t *pPl, int8_t nColumn, int8_t nRow) @@ -290,11 +277,6 @@ uint8_t tetris_playfield_collision(tetris_playfield_t *pPl, } -/* Function: tetris_playfield_advancePiece - * Description: lowers piece by one row or finally docks it - * Argument pPl: playfield to perform action on - * Return value: void - */ void tetris_playfield_advancePiece(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -367,12 +349,6 @@ void tetris_playfield_advancePiece(tetris_playfield_t *pPl) } -/* Function: tetris_playfield_movePiece - * Description: moves piece to the given direction - * Argument pPl: playfield to perform action on - * Argument direction: direction (see tetris_playfield_direction_t) - * Return value: 1 if piece could be moved, 0 otherwise - */ uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl, tetris_playfield_direction_t direction) { @@ -396,12 +372,6 @@ uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl, } -/* Function: tetris_playfield_rotatePiece - * Description: rotates piece to the given direction - * Argument pPl: playfield to perform action on - * Argument r: type of rotation (see tetris_piece_rotation_t) - * Return value: 1 if piece could be rotated, 0 otherwise - */ uint8_t tetris_playfield_rotatePiece(tetris_playfield_t *pPl, tetris_piece_rotation_t rotation) { @@ -436,11 +406,6 @@ uint8_t tetris_playfield_rotatePiece(tetris_playfield_t *pPl, } -/* Function: tetris_playfield_removeCompletedLines - * Description: removes completed lines (if any) and lowers the dump - * Argument pPl: playfield to perform action on - * Return value: void - */ void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -524,11 +489,6 @@ void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl) * get functions * *****************/ -/* Function: tetris_playfield_getWidth - * Description: returns the width of the playfield - * Argument pPl: the playfield we want information from - * Return value: width of the playfield - */ int8_t tetris_playfield_getWidth(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -547,11 +507,6 @@ int8_t tetris_playfield_getHeight(tetris_playfield_t *pPl) } -/* Function: tetris_playfield_getPiece - * Description: returns the currently falling piece - * Argument pPl: the playfield we want information from - * Return value: pointer to the currently falling piece - */ tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -559,11 +514,6 @@ tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl) } -/* Function: tetris_playfield_getColumn - * Description: returns the column of the currently falling piece - * Argument pPl: the playfield we want information from - * Return value: column of the currently falling piece - */ int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -571,11 +521,6 @@ int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl) } -/* Function: tetris_playfield_getRow - * Description: returns the row of the currently falling piece - * Argument pPl: the playfield we want information from - * Return value: row of the currently falling piece - */ int8_t tetris_playfield_getRow(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -583,13 +528,6 @@ int8_t tetris_playfield_getRow(tetris_playfield_t *pPl) } -/* Function: tetris_playfield_getRowMask - * Description: returns the row mask relative to nRow - * Argument pPl: the playfield we want information from - * Return value: the first 4 bits indicate which lines (relative to nRow) - * have been removed if we are in status TETRIS_PFS_READY - * LSB is the highest line - */ uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -597,11 +535,6 @@ uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl) } -/* Function: tetris_playfield_getStatus - * Description: returns the status of the playfield - * Argument pPl: the playfield we want information from - * Return value: status of the playfield (see tetris_playfield_status_t) - */ tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl) { assert(pPl != NULL); @@ -609,12 +542,6 @@ tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl) } -/* Function: tetris_playfield_getDumpRow - * Description: returns the given row of the dump (as bitmap) - * Argument pPl: the playfield we want information from - * Argument nRow: the number of the row (0 <= nRow < height of playfield) - * Return value: bitmap of the requested row (LSB is leftmost column) - */ uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl, int8_t nRow) { @@ -626,13 +553,6 @@ uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl, #ifdef GAME_BASTET -/* Function: tetris_playfield_predictDeepestRow - * Description: returns the deepest possible row of a given piece - * Argument pPl: the playfield on which we want to test a piece - * Argument pPiece: the piece which should be tested - * Argument nColumn: the column where the piece should be dropped - * Return value: the row of the piece (playfield compliant coordinates) - */ int8_t tetris_playfield_predictDeepestRow(tetris_playfield_t *pPl, tetris_piece_t *pPiece, int8_t nColumn) @@ -665,15 +585,7 @@ int8_t tetris_playfield_predictDeepestRow(tetris_playfield_t *pPl, return nRow; } -/* Function: tetris_playfield_predictCompleteLines - * Description: predicts the number of complete lines for a piece at - * a given column - * Argument pPl: the playfield on which we want to test a piece - * Argument pPiece: the piece which should be tested - * Argument nRow: the row where the given piece collides - * Argument nColumn: the column where the piece should be dropped - * Return value: amount of complete lines - */ + int8_t tetris_playfield_predictCompleteLines(tetris_playfield_t *pPl, tetris_piece_t *pPiece, int8_t nRow, @@ -724,17 +636,6 @@ int8_t tetris_playfield_predictCompleteLines(tetris_playfield_t *pPl, } -/* Function: tetris_playfield_predictBottomRow - * Description: predicts the appearance of the bottom row of the - * playfield (for a piece at a given column) and - * initializes an iterator structure - * Argument pIt: [out] a pointer to an iterator which should be initialized - * Argument pPl: the playfield on which we want to test a piece - * Argument pPiece: the piece which should be tested - * Argument nRow: the row where the given piece collides - * Argument nColumn: the column where the piece should be dropped - * Return value: appearance of the predicted dump row at the bottom - */ uint16_t* tetris_playfield_predictBottomRow(tetris_playfield_iterator_t *pIt, tetris_playfield_t *pPl, tetris_piece_t *pPiece, @@ -761,12 +662,6 @@ uint16_t* tetris_playfield_predictBottomRow(tetris_playfield_iterator_t *pIt, } -/* Function: tetris_playfield_predictNextRow - * Description: predicts the appearance of the next row of the playfield - * (for a given iterator) - * Argument pIt: a pointer to a dump iterator - * Return value: appearance of next predicted row (or NULL -> no next line) - */ uint16_t* tetris_playfield_predictNextRow(tetris_playfield_iterator_t *pIt) { uint16_t nPieceMap = 0; diff --git a/games/tetris/playfield.h b/games/tetris/playfield.h index a3a7170..3d68c85 100644 --- a/games/tetris/playfield.h +++ b/games/tetris/playfield.h @@ -22,11 +22,11 @@ tetris_playfield_direction_t; // status of the playfield typedef enum tetris_playfield_status_t { - TETRIS_PFS_READY, // ready to get next piece - TETRIS_PFS_HOVERING, // piece is still hovering - TETRIS_PFS_GLIDING, // piece is gliding on the dump - TETRIS_PFS_DOCKED, // piece has been docked - TETRIS_PFS_GAMEOVER // playfield is filled up + TETRIS_PFS_READY, /** ready to get next piece */ + TETRIS_PFS_HOVERING, /** piece is still hovering */ + TETRIS_PFS_GLIDING, /** piece is gliding on the dump */ + TETRIS_PFS_DOCKED, /** piece has been docked */ + TETRIS_PFS_GAMEOVER /** playfield is filled up */ } tetris_playfield_status_t; @@ -34,15 +34,15 @@ tetris_playfield_status_t; // tetris_playfield_t typedef struct tetris_playfield_t { - int8_t nWidth; // width of playfield - int8_t nHeight; // height of playfield - tetris_piece_t *pPiece; // currently falling piece - int8_t nColumn; // horz. piece pos. (0 is left) - int8_t nRow; // vert. piece pos. (0 is top) - uint8_t nRowMask; // removed lines relative to nRow (bitmap) - tetris_playfield_status_t status; // status - int8_t nFirstMatterRow; // first row (from top) which contains matter - uint16_t *dump; // playfield itself + int8_t nWidth; /** width of playfield */ + int8_t nHeight; /** height of playfield */ + tetris_piece_t *pPiece; /** currently falling piece */ + int8_t nColumn; /** horz. piece pos. (0 is left) */ + int8_t nRow; /** vert. piece pos. (0 is top) */ + uint8_t nRowMask; /** removed lines relative to nRow */ + tetris_playfield_status_t status; /** status */ + int8_t nFirstMatterRow; /** first row from top which has matter */ + uint16_t *dump; /** playfield itself */ } tetris_playfield_t; @@ -50,15 +50,15 @@ tetris_playfield_t; // iterator for predicted dump rows typedef struct tetris_playfield_iterator_t { - tetris_playfield_t *pPlayfield; // playfield to be examined - tetris_piece_t *pPiece; // piece which should be tested - int8_t nColumn; // the column where the piece should be dropped - uint16_t nFullRow; // value of a full row - int8_t nCurrentRow; // the actual row in the playfield - int8_t nPieceHighestRow; // the highest row index of the piece - int8_t nPieceLowestRow; // the lowest row index of the piece - int8_t nStopRow; // the last row to be examined - uint16_t nRowBuffer; // internal buffer for returned row values + tetris_playfield_t *pPlayfield; /** playfield to be examined */ + tetris_piece_t *pPiece; /** piece which should be tested */ + int8_t nColumn; /** column where piece should be dropped */ + uint16_t nFullRow; /** value of a full row */ + int8_t nCurrentRow; /** the actual row in the playfield */ + int8_t nPieceHighestRow; /** the highest row index of the piece */ + int8_t nPieceLowestRow; /** the lowest row index of the piece */ + int8_t nStopRow; /** the last row to be examined */ + uint16_t nRowBuffer; /** internal buffer for returned rows */ } tetris_playfield_iterator_t; @@ -67,19 +67,18 @@ tetris_playfield_iterator_t; * construction/destruction * ****************************/ -/* Function: tetris_playfield_construct - * Description: constructs a playfield with the given diemensions - * Argument nWidth: width of playfield (4 <= n <= 16) - * Argument nHeight: height of playfield (4 <= n <= 124) - * Return value: pointer to a newly created playfield +/** + * constructs a playfield with the given diemensions + * @param nWidth width of playfield (4 <= n <= 16) + * @param nHeight height of playfield (4 <= n <= 124) + * @return pointer to a newly created playfield */ tetris_playfield_t *tetris_playfield_construct(int8_t nWidth, int8_t nHeight); -/* Function: tetris_playfield_destruct - * Description: destructs a playfield - * Argument pPl: pointer to the playfield to be destructed - * Return value: void +/** + * destructs a playfield + * @param pPl pointer to the playfield to be destructed */ void tetris_playfield_destruct(tetris_playfield_t *pPl); @@ -88,70 +87,74 @@ void tetris_playfield_destruct(tetris_playfield_t *pPl); * playfield related functions * *******************************/ -/* Function: tetris_playfield_reset - * Description: resets playfield to begin a new game - * Argument pPl: playfield to perform action on - * Return value: void +/** + * calculates number of lines for the given row mask + * @param nRowMask row mask from which the no. of lines will be calculated + * @return number of lines of the row mask + */ +uint8_t tetris_playfield_calculateLines(uint8_t nRowMask); + + +/** + * resets playfield to begin a new game + * @param pPl playfield to perform action on */ void tetris_playfield_reset(tetris_playfield_t *pPl); -/* Function: tetris_playfield_insertPiece - * Description: inserts a new piece - * Argument pPl: playfield to perform action on - * Argument pPiece: piece to be inserted - * Argument ppOldPiece: [out] indirect pointer to former piece for deallocation - * Return value: void +/** + * inserts a new piece + * @param pPl playfield to perform action on + * @param pPiece piece to be inserted + * @param ppOldPiece [out] indirect pointer to former piece for deallocation */ void tetris_playfield_insertPiece(tetris_playfield_t *pPl, tetris_piece_t *pPiece, tetris_piece_t** ppOldPiece); -/* Function: tetris_playfield_collision - * Description: detects if piece collides with s.th. at a given position - * Argument pPl: playfield to perform action on - * Argument nColumn: column where the piece should be moved - * Argument nRow: row where the piece should be moved - * Return value: 1 for collision, 0 otherwise +/** + * detects if piece collides with s.th. at a given position + * @param pPl playfield to perform action on + * @param nColumn column where the piece should be moved + * @param nRow row where the piece should be moved + * @return 1 for collision, 0 otherwise */ uint8_t tetris_playfield_collision(tetris_playfield_t *pPl, int8_t nColumn, int8_t nRow); -/* Function: tetris_playfield_advancePiece - * Description: lowers piece by one row or finally docks it - * Argument pPl: playfield to perform action on - * Return value: void +/** + * lowers piece by one row or finally docks it + * @param pPl playfield to perform action on */ void tetris_playfield_advancePiece(tetris_playfield_t *pPl); -/* Function: tetris_playfield_movePiece - * Description: moves piece to the given direction - * Argument pPl: playfield to perform action on - * Argument direction: direction (see tetris_playfield_direction_t) - * Return value: 1 if piece could be moved, 0 otherwise +/** + * moves piece to the given direction + * @param pPl playfield to perform action on + * @param direction direction (see tetris_playfield_direction_t) + * @return 1 if piece could be moved, 0 otherwise */ uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl, tetris_playfield_direction_t direction); -/* Function: tetris_playfield_rotatePiece - * Description: rotates piece to the given direction - * Argument pPl: playfield to perform action on - * Argument r: type of rotation (see tetris_piece_rotation_t) - * Return value: 1 if piece could be rotated, 0 otherwise +/** + * rotates piece to the given direction + * @param pPl playfield to perform action on + * @param r type of rotation (see tetris_piece_rotation_t) + * @return 1 if piece could be rotated, 0 otherwise */ uint8_t tetris_playfield_rotatePiece(tetris_playfield_t *pPl, tetris_piece_rotation_t rotation); -/* Function: tetris_playfield_removeCompletedLines - * Description: removes completed lines (if any) and lowers the dump - * Argument pPl: playfield to perform action on - * Return value: void +/** + * removes completed lines (if any) and lowers the dump + * @param pPl playfield to perform action on */ void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl); @@ -160,68 +163,67 @@ void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl); * get functions * *****************/ -/* Function: tetris_playfield_getWidth - * Description: returns the width of the playfield - * Argument pPl: the playfield we want information from - * Return value: width of the playfield +/** + * returns the width of the playfield + * @param pPl the playfield we want information from + * @return width of the playfield */ int8_t tetris_playfield_getWidth(tetris_playfield_t *pPl); -/* Function: tetris_playfield_getHeight - * Description: returns the height of the playfield - * Argument pPl: the playfield we want information from - * Return value: height of the playfield +/** + * returns the height of the playfield + * @param pPl the playfield we want information from + * @return height of the playfield */ int8_t tetris_playfield_getHeight(tetris_playfield_t *pPl); -/* Function: tetris_playfield_getPiece - * Description: returns the currently falling piece - * Argument pPl: the playfield we want information from - * Return value: pointer to the currently falling piece +/** + * returns the currently falling piece + * @param pPl the playfield we want information from + * @return pointer to the currently falling piece */ tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl); -/* Function: tetris_playfield_getColumn - * Description: returns the column of the currently falling piece - * Argument pPl: the playfield we want information from - * Return value: column of the currently falling piece +/** + * returns the column of the currently falling piece + * @param pPl the playfield we want information from + * @return column of the currently falling piece */ int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl); -/* Function: tetris_playfield_getRow - * Description: returns the row of the currently falling piece - * Argument pPl: the playfield we want information from - * Return value: row of the currently falling piece +/** + * returns the row of the currently falling piece + * @param pPl the playfield we want information from + * @return row of the currently falling piece */ int8_t tetris_playfield_getRow(tetris_playfield_t *pPl); -/* Function: tetris_playfield_getRowMask - * Description: returns the row mask relative to nRow - * Argument pPl: the playfield we want information from - * Return value: the first 4 bits indicate which lines (relative to nRow) - * have been removed if we are in status TETRIS_PFS_READY +/** + * returns the row mask relative to nRow + * @param pPl the playfield we want information from + * @return bit mask of removed lines (relative to current position) */ uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl); -/* Function: tetris_playfield_getStatus - * Description: returns the status of the playfield - * Argument pPl: the playfield we want information from - * Return value: status of the playfield (see tetris_playfield_status_t) +/** + * returns the status of the playfield + * @param pPl the playfield we want information from + * @return status of the playfield (see tetris_playfield_status_t) */ tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl); -/* Function: tetris_playfield_getDumpRow - * Description: returns the given row of the dump (as bitmap) - * Argument pPl: the playfield we want information from - * Argument nRow: the number of the row (0 <= nRow <= 124) - * Return value: bitmap of the requested row (LSB is leftmost column) +/** + * returns the given row of the dump (as bitmap) + * @param pPl the playfield we want information from + * @param nRow the number of the row (0 <= nRow <= 124) + * @return bitmap of the requested row (LSB is leftmost column) */ uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl, int8_t nRow); @@ -229,26 +231,25 @@ uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl, #ifdef GAME_BASTET -/* Function: tetris_playfield_predictDeepestRow - * Description: returns the deepest possible row of a given piece - * Argument pPl: the playfield on which we want to test a piece - * Argument pPiece: the piece which should be tested - * Argument nColumn: the column where the piece should be dropped - * Return value: the row of the piece (playfield compliant coordinates) +/** + * returns the deepest possible row for a given piece + * @param pPl the playfield on which we want to test a piece + * @param pPiece the piece which should be tested + * @param nColumn the column where the piece should be dropped + * @return the row of the piece (playfield compliant coordinates) */ int8_t tetris_playfield_predictDeepestRow(tetris_playfield_t *pPl, tetris_piece_t *pPiece, int8_t nColumn); -/* Function: tetris_playfield_predictCompleteLines - * Description: predicts the number of complete lines for a piece at - * a given column - * Argument pPl: the playfield on which we want to test a piece - * Argument pPiece: the piece which should be tested - * Argument nRow: the row where the given piece collides - * Argument nColumn: the column where the piece should be dropped - * Return value: amount of complete lines +/** + * predicts the number of complete lines for a piece at a given column + * @param pPl the playfield on which we want to test a piece + * @param pPiece the piece which should be tested + * @param nRow the row where the given piece collides + * @param nColumn the column where the piece should be dropped + * @return amount of complete lines */ int8_t tetris_playfield_predictCompleteLines(tetris_playfield_t *pPl, tetris_piece_t *pPiece, @@ -256,16 +257,14 @@ int8_t tetris_playfield_predictCompleteLines(tetris_playfield_t *pPl, int8_t nColumn); -/* Function: tetris_playfield_predictBottomRow - * Description: predicts the appearance of the bottom row of the - * playfield (for a piece at a given column) and - * initializes an iterator structure - * Argument pIt: a pointer to an iterator which should be initialized - * Argument pPl: the playfield on which we want to test a piece - * Argument pPiece: the piece which should be tested - * Argument nRow: the row where the given piece collides - * Argument nColumn: the column where the piece should be dropped - * Return value: appearance of the predicted dump row at the bottom +/** + * predicts appearance of the bottom row and initializes an iterator structure + * @param pIt a pointer to an iterator which should be initialized + * @param pPl the playfield on which we want to test a piece + * @param pPiece the piece which should be tested + * @param nRow the row where the given piece collides + * @param nColumn the column where the piece should be dropped + * @return appearance of the predicted dump row at the bottom as bit mask */ uint16_t* tetris_playfield_predictBottomRow(tetris_playfield_iterator_t *pIt, tetris_playfield_t *pPl, @@ -274,11 +273,10 @@ uint16_t* tetris_playfield_predictBottomRow(tetris_playfield_iterator_t *pIt, int8_t nColumn); -/* Function: tetris_playfield_predictNextRow - * Description: predicts the appearance of the next row of the playfield - * (for a given iterator) - * Argument pIt: a pointer to a dump iterator - * Return value: appearance of next predicted row (or NULL -> no next line) +/** + * predicts appearance of the next row of the playfield (for a given iterator) + * @param pIt a pointer to a dump iterator + * @return appearance of next predicted row (or NULL -> no next line) */ uint16_t* tetris_playfield_predictNextRow(tetris_playfield_iterator_t *pIt); diff --git a/games/tetris/tetris_main.c b/games/tetris/tetris_main.c new file mode 100644 index 0000000..4c02a5c --- /dev/null +++ b/games/tetris/tetris_main.c @@ -0,0 +1,205 @@ +#include +#include +#include +#include + +#include "tetris_main.h" +#include "variants.h" +#include "piece.h" +#include "playfield.h" +#include "view.h" +#include "input.h" +#include "highscore.h" + + +void tetris_main(const tetris_variant_t *const pVariantMethods) +{ + // get view dependent dimensions of the playfield + int8_t nWidth; + int8_t nHeight; + tetris_view_getDimensions(&nWidth, &nHeight); + + // holds the current user command which should be processed + tetris_input_command_t inCmd; + + // prepare data structures that drive the game... + tetris_playfield_t *pPl = tetris_playfield_construct(nWidth, nHeight); + void *pVariantData = pVariantMethods->construct(pPl); + tetris_input_t *pIn = tetris_input_construct(); + tetris_view_t *pView = tetris_view_construct(pVariantMethods, + pVariantData, pPl); + + // retrieve highscore + tetris_highscore_index_t nHighscoreIndex = + pVariantMethods->getHighscoreIndex(pVariantData); + uint16_t nHighscore = + tetris_highscore_retrieveHighscore(nHighscoreIndex); + uint16_t nHighscoreName = + tetris_highscore_retrieveHighscoreName(nHighscoreIndex); + + // the view only monitors the variant data and the playfield object for the + // game status so we must put information like the next piece or the current + // highscore to a place where the view can find it + pVariantMethods->setHighscore(pVariantData, nHighscore); + pVariantMethods->setHighscoreName(pVariantData, nHighscoreName); + + int8_t nPieceRow; // for determining skipped lines after a piece drop + tetris_piece_t *pPiece = NULL; // initialize piece + tetris_input_pace_t inPace; // pace flag + + // game loop, runs as long as the game is not over + while (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER) + { + // what we do strongly depends on the status of the playfield + switch (tetris_playfield_getStatus(pPl)) + { + // the playfield awaits a new piece + case TETRIS_PFS_READY: + pPiece = pVariantMethods->choosePiece(pVariantData); + tetris_piece_t *pOldPiece; + tetris_playfield_insertPiece(pPl, pPiece, &pOldPiece); + // destruct old piece (if it exists) since we don't need it anymore + if (pOldPiece != NULL) + { + tetris_piece_destruct(pOldPiece); + pOldPiece = NULL; + } + break; + + // a piece is hovering and can be controlled by the player + case TETRIS_PFS_HOVERING: + case TETRIS_PFS_GLIDING: + // if the piece is gliding the input module has to grant us + // a minimum amount of time to move it + if (tetris_playfield_getStatus(pPl) == TETRIS_PFS_GLIDING) + { + inPace = TETRIS_INPACE_GLIDING; + } + else + { + inPace = TETRIS_INPACE_HOVERING; + } + + // ensure correct view mode if the game isn't paused + if ((inCmd = tetris_input_getCommand(pIn, inPace)) + != TETRIS_INCMD_PAUSE) + { + tetris_view_setViewMode(pView, TETRIS_VIMO_RUNNING); + } + + // what we do depends on what the input module tells us + switch (inCmd) + { + // game paused? + case TETRIS_INCMD_PAUSE: + // tell the view it should display the pause screen + tetris_view_setViewMode(pView, TETRIS_VIMO_PAUSED); + break; + + // the piece was pulled down by the almighty gravity + case TETRIS_INCMD_GRAVITY: + tetris_playfield_advancePiece(pPl); + break; + + // the player has pulled down the piece herself/himself + case TETRIS_INCMD_DOWN: + tetris_playfield_advancePiece(pPl); + // if the game still runs, reward the player with extra points + if (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER) + { + pVariantMethods->singleDrop(pVariantData, 1); + } + break; + + // player shifted the piece to the left + case TETRIS_INCMD_LEFT: + tetris_playfield_movePiece(pPl, TETRIS_PFD_LEFT); + break; + + // player shifted the piece to the right + case TETRIS_INCMD_RIGHT: + tetris_playfield_movePiece(pPl, TETRIS_PFD_RIGHT); + break; + + // player rotated the piece clockwise + case TETRIS_INCMD_ROT_CW: + tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CW); + break; + + // player rotated the piece counter clockwise + case TETRIS_INCMD_ROT_CCW: + tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CCW); + break; + + // the player decided to make an immediate drop + case TETRIS_INCMD_DROP: + nPieceRow = tetris_playfield_getRow(pPl); + // emulate immediate drop + while((tetris_playfield_getStatus(pPl) == TETRIS_PFS_GLIDING) || + (tetris_playfield_getStatus(pPl) == TETRIS_PFS_HOVERING)) + { + tetris_playfield_advancePiece(pPl); + } + // if the game still runs, reward the player with extra points + if (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER) + { + pVariantMethods->completeDrop(pVariantData, + tetris_playfield_getRow(pPl) - nPieceRow); + } + break; + + // avoid compiler warnings + default: + break; + } + + pVariantMethods->setLastInput(pVariantData, inCmd); + tetris_input_setOrientation(pIn, + pVariantMethods->getOrientation(pVariantData)); + + break; + + // the piece has irrevocably hit the ground + case TETRIS_PFS_DOCKED: + // avoid accidentally issued "down" commands + tetris_input_resetDownKeyRepeat(pIn); + + // remove complete lines (if any) + tetris_playfield_removeCompleteLines(pPl); + + // let the variant object decide how many points the player gets and + // whether the level gets changed + pVariantMethods->removedLines(pVariantData, + tetris_playfield_getRowMask(pPl)); + tetris_input_setLevel(pIn, pVariantMethods->getLevel(pVariantData)); + break; + + // avoid compiler warnings + default: + break; + } + + // the view updates its state every loop cycle to make changes visible + tetris_view_update(pView); + } + + // game is over and we provide the player with her/his results + tetris_view_showResults(pView); + + // update highscore if it has been beaten + uint16_t nScore = pVariantMethods->getScore(pVariantData); + if (nScore > nHighscore) + { + nHighscore = nScore; + nHighscoreName = tetris_highscore_inputName(); + tetris_highscore_saveHighscore(nHighscoreIndex, nHighscore); + tetris_highscore_saveHighscoreName(nHighscoreIndex, nHighscoreName); + } + + // cleanup + tetris_view_destruct(pView); + tetris_input_destruct(pIn); + pVariantMethods->destruct(pVariantData); + tetris_playfield_destruct(pPl); + tetris_piece_destruct(pPiece); +} diff --git a/games/tetris/tetris_main.h b/games/tetris/tetris_main.h new file mode 100644 index 0000000..aeb5602 --- /dev/null +++ b/games/tetris/tetris_main.h @@ -0,0 +1,14 @@ +#ifndef TETRIS_MAIN_H_ +#define TETRIS_MAIN_H_ + +#include "variants.h" + + +/** + * runs the tetris game + * @param pVariantMethods struct of function pointers for a game variant + */ +void tetris_main(const tetris_variant_t *const pVariantMethods); + + +#endif /* TETRIS_MAIN_H_ */ diff --git a/games/tetris/tetrisfp.h b/games/tetris/tetrisfp.h deleted file mode 100644 index 5e873fc..0000000 --- a/games/tetris/tetrisfp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef TETRISFP_H_ -#define TETRISFP_H_ - -extern uint8_t tetris_screendir; - -#endif /* BAST_H_ */ diff --git a/games/tetris/variant_bastet.c b/games/tetris/variant_bastet.c new file mode 100644 index 0000000..07674b1 --- /dev/null +++ b/games/tetris/variant_bastet.c @@ -0,0 +1,416 @@ +#include +#include +#include +#include + +#include "../../random/prng.h" +#include "../../compat/pgmspace.h" +#include "../../menu/menu.h" +#include "variant_bastet.h" +#include "variants.h" +#include "tetris_main.h" +#include "input.h" +#include "piece.h" +#include "playfield.h" +#include "orientation.h" +#include "input.h" + + +/*************************** + * non-interface functions * + ***************************/ + +/** + * resets the array for the column heights + * @param pBastet bastet instance whose array should be reset + * @param nStart start index + * @param nStop stop index + */ +void tetris_bastet_clearColHeights(tetris_bastet_variant_t *pBastet, + int8_t nStart, + int8_t nStop) +{ + for (int i = nStart; i <= nStop; ++i) + { + pBastet->pColHeights[i] = 0; + } +} + + +/** + * compare function for quick sorting the pieces by score + * @param pa the first value to compare + * @param pb the second value to compare + */ +int tetris_bastet_qsortCompare(const void *pa, const void *pb) +{ + tetris_bastet_scorepair_t *pScorePairA = (tetris_bastet_scorepair_t *)pa; + tetris_bastet_scorepair_t *pScorePairB = (tetris_bastet_scorepair_t *)pb; + if (pScorePairA->nScore == pScorePairB->nScore) + { + return 0; + } + else if (pScorePairA->nScore < pScorePairB->nScore) + { + return -1; + } + else + { + return 1; + } +} + + +/*************** + * entry point * + ***************/ + +#ifdef MENU_SUPPORT +// Bastet icon, MSB is leftmost pixel +static uint8_t bastet_icon[8] PROGMEM = + { 0x81, 0xc3, 0xff, 0x99, 0xff, 0xff, 0x66, 0x3c }; +game_descriptor_t bastet_game_descriptor + __attribute__((section(".game_descriptors"))) = +{ + &tetris_bastet, + bastet_icon, +}; +#endif + + +void tetris_bastet(void) +{ + tetris_main(&tetrisBastetVariant); +} + + +/**************************** + * construction/destruction * + ****************************/ + +const tetris_variant_t tetrisBastetVariant = +{ + &tetris_bastet_construct, + &tetris_bastet_destruct, + &tetris_bastet_choosePiece, + &tetris_bastet_singleDrop, + &tetris_bastet_completeDrop, + &tetris_bastet_removedLines, + &tetris_bastet_getScore, + &tetris_bastet_getHighscore, + &tetris_bastet_setHighscore, + &tetris_bastet_getHighscoreName, + &tetris_bastet_setHighscoreName, + &tetris_bastet_getLevel, + &tetris_bastet_getLines, + &tetris_bastet_getPreviewPiece, + &tetris_bastet_getHighscoreIndex, + &tetris_bastet_setLastInput, + &tetris_bastet_getOrientation +}; + + +void *tetris_bastet_construct(tetris_playfield_t *pPl) +{ + tetris_bastet_variant_t *pBastet = + (tetris_bastet_variant_t *) malloc(sizeof(tetris_bastet_variant_t)); + memset(pBastet, 0, sizeof(tetris_bastet_variant_t)); + + pBastet->pPlayfield = pPl; + + int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); + pBastet->pColHeights = (int8_t*) calloc(nWidth, sizeof(int8_t)); + tetris_bastet_clearColHeights(pBastet, 0, nWidth - 1); + + return pBastet; +} + + +void tetris_bastet_destruct(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastetVariant = + (tetris_bastet_variant_t *)pVariantData; + if (pBastetVariant->pColHeights != NULL) + { + free(pBastetVariant->pColHeights); + } + if (pBastetVariant->pPreviewPiece != NULL) + { + tetris_piece_destruct(pBastetVariant->pPreviewPiece); + } + + free(pBastetVariant); +} + + +/**************************** + * bastet related functions * + ****************************/ + +int16_t tetris_bastet_evaluateMove(tetris_bastet_variant_t *pBastet, + tetris_piece_t *pPiece, + int8_t nColumn) +{ + // the row where the given piece collides + int8_t nDeepestRow = tetris_playfield_predictDeepestRow(pBastet->pPlayfield, + pPiece, nColumn); + + // initial score of the given piece + int16_t nScore = -32000; + + // modify score based on complete lines + int8_t nLines = tetris_playfield_predictCompleteLines(pBastet->pPlayfield, + pPiece, nDeepestRow, nColumn); + nScore += 5000 * nLines; + + // determine sane start and stop columns whose heights we want to calculate + int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); + int8_t nStartCol = ((nColumn - 1) < 0) ? 0 : nColumn - 1; + int8_t nStopCol; + // Do we start at the left most position? + // If we do we MUST calculate the heights of ALL columns (initial step) + if (nColumn <= -3) + { + nStopCol = nWidth - 1; + // reset all column heights to zero + tetris_bastet_clearColHeights(pBastet, 0 , nWidth); + } + // If not, only calculate columns which are affected by the moved piece. + else + { + nStopCol = (nColumn + 3) < nWidth ? nColumn + 3 : nWidth - 1; + // clear affected column heights to prevent miscalculations + tetris_bastet_clearColHeights(pBastet, nStartCol, nStopCol); + } + + // go through every row and calculate column heights + tetris_playfield_iterator_t iterator; + int8_t nHeight = 1; + uint16_t *pDump = tetris_playfield_predictBottomRow(&iterator, + pBastet->pPlayfield, pPiece, nDeepestRow, nColumn); + if (pDump == NULL) + { + // an immediately returned NULL is caused by a full dump -> low score + return -32766; + } + while (pDump != NULL) + { + uint16_t nColMask = 0x0001 << nStartCol; + for (int x = nStartCol; x <= nStopCol; ++x) + { + if ((*pDump & nColMask) != 0) + { + pBastet->pColHeights[x] = nHeight; + } + nColMask <<= 1; + } + pDump = tetris_playfield_predictNextRow(&iterator); + ++nHeight; + } + // modify score based on predicted column heights + for (int x = 0; x < nWidth; ++x) + { + nScore -= 5 * pBastet->pColHeights[x]; + } + + return nScore; +} + + +void tetris_bastet_evaluatePieces(tetris_bastet_variant_t *pBastet) +{ + int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); + tetris_piece_t *pPiece = tetris_piece_construct(TETRIS_PC_LINE, + TETRIS_PC_ANGLE_0); + for (int8_t nBlock = TETRIS_PC_LINE; nBlock <= TETRIS_PC_Z; ++nBlock) + { + int16_t nMaxScore = -32768; + tetris_piece_setShape(pPiece, nBlock); + int8_t nAngleCount = tetris_piece_getAngleCount(pPiece); + for (int8_t nAngle = TETRIS_PC_ANGLE_0; nAngle < nAngleCount; ++nAngle) + { + tetris_piece_setAngle(pPiece, nAngle); + for (int8_t nCol = -3; nCol < nWidth; ++nCol) + { + int16_t nScore = tetris_bastet_evaluateMove(pBastet, + pPiece, nCol); + nMaxScore = nMaxScore > nScore ? nMaxScore : nScore; + } + } + pBastet->nPieceScores[nBlock].shape = nBlock; + pBastet->nPieceScores[nBlock].nScore = nMaxScore; + } + tetris_piece_destruct(pPiece); +} + + +tetris_piece_t* tetris_bastet_choosePiece(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastet = + (tetris_bastet_variant_t *)pVariantData; + + // determine the best score for every piece + tetris_bastet_evaluatePieces(pBastet); + // perturb score (-2 to +2) to avoid stupid tie handling + for (uint8_t i = 0; i < 7; ++i) + { + pBastet->nPieceScores[i].nScore += random8() % 5 - 2; + } + + // sort pieces by their score in ascending order + qsort(pBastet->nPieceScores, 7, sizeof(tetris_bastet_scorepair_t), + &tetris_bastet_qsortCompare); + + // new "preview" piece (AKA "won't give you this one") + if (pBastet->pPreviewPiece != NULL) + { + tetris_piece_destruct(pBastet->pPreviewPiece); + } + pBastet->pPreviewPiece = + tetris_piece_construct(pBastet->nPieceScores[6].shape, + TETRIS_PC_ANGLE_0); + + tetris_piece_t *pPiece; + const uint8_t nPercent[4] = {75, 92, 98, 100}; + uint8_t nRnd = rand() % 100; + for (uint8_t i = 0; i < 4; ++i) + { + if (nRnd < nPercent[i]) + { + pPiece = tetris_piece_construct(pBastet->nPieceScores[i].shape, + TETRIS_PC_ANGLE_0); + break; + } + } + return pPiece; +} + + +void tetris_bastet_singleDrop(void *pVariantData, + uint8_t nLines) +{ + return; +} + + +void tetris_bastet_completeDrop(void *pVariantData, + uint8_t nLines) +{ + return; +} + + +void tetris_bastet_removedLines(void *pVariantData, + uint8_t nRowMask) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastet = + (tetris_bastet_variant_t *)pVariantData; + uint8_t nLines = tetris_playfield_calculateLines(nRowMask); + + pBastet->nLines += nLines; + pBastet->nLevel = ((pBastet->nLines / 10) < TETRIS_INPUT_LEVELS) ? + (pBastet->nLines / 10) : (TETRIS_INPUT_LEVELS - 1); + + pBastet->nScore += nLines; + return; +} + + +/***************** + * get functions * + *****************/ + +uint16_t tetris_bastet_getScore(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastetVariant = + (tetris_bastet_variant_t *)pVariantData; + return pBastetVariant->nScore; +} + + +uint16_t tetris_bastet_getHighscore(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastetVariant = + (tetris_bastet_variant_t *)pVariantData; + return pBastetVariant->nHighscore; +} + + +void tetris_bastet_setHighscore(void *pVariantData, + uint16_t nHighscore) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastetVariant = + (tetris_bastet_variant_t *)pVariantData; + pBastetVariant->nHighscore = nHighscore; +} + + +uint16_t tetris_bastet_getHighscoreName(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastetVariant = + (tetris_bastet_variant_t *)pVariantData; + return pBastetVariant->nHighscoreName; +} + + +void tetris_bastet_setHighscoreName(void *pVariantData, + uint16_t nHighscoreName) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastetVariant = + (tetris_bastet_variant_t *)pVariantData; + pBastetVariant->nHighscoreName = nHighscoreName; +} + + +uint8_t tetris_bastet_getLevel(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastet = + (tetris_bastet_variant_t *)pVariantData; + return pBastet->nLevel; +} + + +uint16_t tetris_bastet_getLines(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastet = + (tetris_bastet_variant_t *)pVariantData; + return pBastet->nLines; +} + + +tetris_piece_t* tetris_bastet_getPreviewPiece(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_bastet_variant_t *pBastetVariant = + (tetris_bastet_variant_t *)pVariantData; + return pBastetVariant->pPreviewPiece; +} + + +tetris_highscore_index_t tetris_bastet_getHighscoreIndex(void *pVariantData) +{ + return TETRIS_HISCORE_BASTET; +} + + +void tetris_bastet_setLastInput(void *pVariantData, + tetris_input_command_t inCmd) +{ + return; +} + + +tetris_orientation_t tetris_bastet_getOrientation(void *pVariantData) +{ + return TETRIS_ORIENTATION_0; +} diff --git a/games/tetris/variant_bastet.h b/games/tetris/variant_bastet.h new file mode 100644 index 0000000..12e658f --- /dev/null +++ b/games/tetris/variant_bastet.h @@ -0,0 +1,218 @@ +#ifndef BAST_H_ +#define BAST_H_ + +#include +#include "variants.h" +#include "playfield.h" +#include "piece.h" +#include "orientation.h" +#include "input.h" + +/*************** + * entry point * + ***************/ + +/** + * runs the Bastet game + */ +void tetris_bastet(void); + + +/********* + * types * + *********/ + +typedef struct tetris_bastet_scorepair_t +{ + tetris_piece_shape_t shape; + int16_t nScore; +} +tetris_bastet_scorepair_t; + + +typedef struct tetris_bastet_variant_t +{ + uint16_t nScore; /** score of the player */ + uint16_t nHighscore; /** highscore */ + uint16_t nHighscoreName; /** champion's initials */ + uint8_t nLevel; /** current level */ + uint16_t nLines; /** number of completed lines */ + tetris_piece_t *pPreviewPiece; /** the piece for the preview */ + tetris_playfield_t *pPlayfield; /** playfield to be examined */ + int8_t *pColHeights; /** calculated heights */ + tetris_bastet_scorepair_t nPieceScores[7]; /** score for every piece */ +} +tetris_bastet_variant_t; + +const tetris_variant_t tetrisBastetVariant; + + +/**************************** + * construction/destruction * + ****************************/ + +/** + * constructs a bastet instance for a given playfield + * @param pPlayfield the playfield to be observed + * @return pointer to a newly created bastet instance + */ +void* tetris_bastet_construct(tetris_playfield_t *pPl); + + +/** + * destructs the given bastet instance + * @param pVariantData the bastet instance to be destroyed + */ +void tetris_bastet_destruct(void *pVariantData); + + +/**************************** + * bastet related functions * + ****************************/ + +/** + * calculates a score for a piece at a given column + * @param pBastet the bastet instance of interest + * @param pPiece the piece to be tested + * @param pnColum the column where the piece should be dropped + * @return score for the given move + */ +int16_t tetris_bastet_evaluateMove(tetris_bastet_variant_t *pBastet, + tetris_piece_t *pPiece, + int8_t nColumn); + + +/** + * calculates the best possible score for every piece + * @param pBastet the bastet instance of interest + */ +void tetris_bastet_evaluatePieces(tetris_bastet_variant_t *pBastet); + + +/** + * chooses a new worst possible piece + * @param pVariantData the variant instance of interest + * @return a tetris piece + */ +tetris_piece_t* tetris_bastet_choosePiece(void *pBastet); + + +/** + * chooses a new (best possible) piece for the preview + * @param pVariantData the variant instance of interest + * @return a tetris piece + */ +tetris_piece_t* tetris_bastet_choosePreviewPiece(void *pBastet); + + +/** + * add points which result from single step dropping + * @param pVariantData the variant data object we want to modify + * @param nLines the number of rows involved + */ +void tetris_bastet_singleDrop(void *pVariantData, + uint8_t nLines); + + +/** + * add points which result from a complete drop + * @param pVariantData the variant data object we want to modify + * @param nLines the number of rows involved + */ +void tetris_bastet_completeDrop(void *pVariantData, + uint8_t nLines); + + +/** + * add points which result from removed rows + * @param pVariantData the variant data object we want to modify + * @param nRowMask bit mask of removed lines + */ +void tetris_bastet_removedLines(void *pVariantData, + uint8_t nRowMask); + + +/********************* + * get/set functions * + *********************/ + +/** + * returns the current score + * @param pVariantData variant data object we want information from + * @return score + */ +uint16_t tetris_bastet_getScore(void *pVariantData); + + +/** + * returns the current highscore + * @param pVariantData variant data object we want information from + * @return highscore + */ +uint16_t tetris_bastet_getHighscore(void *pVariantData); + + +/** + * set highscore + * @param pVariantData variant data object we want to modify + * @param nHighscore highscore + */ +void tetris_bastet_setHighscore(void *pVariantData, + uint16_t nHighscore); + + +/** + * returns the current highscore name + * @param pVariantData variant data object we want information from + * @return champion's name packed as uint16_t + */ +uint16_t tetris_bastet_getHighscoreName(void *pVariantData); + + +/** + * set highscore name + * @param pVariantData the variant data object we want to modify + * @param nHighscoreName champion's name packed as uint16_t + */ +void tetris_bastet_setHighscoreName(void *pVariantData, + uint16_t nHighscoreName); + + +/** + * returns the current level + * @param pVariantData variant data object we want information from + * @return the level as uint8_t + */ +uint8_t tetris_bastet_getLevel(void *pVariantData); + + +/** + * returns the number of completed lines + * @param pVariantData the variant data object we want information from + * @return number of completed lines + */ +uint16_t tetris_bastet_getLines(void *pVariantData); + + +/** + * returns piece which was set via tetris_std_setPreviewPiece + * @param pVariantData the variant data object we want information from + * @return the piece intended to be the next one + */ +tetris_piece_t* tetris_bastet_getPreviewPiece(void *pVariantData); + + +/** + * retrieves the variant's highscore index + * @param pVariantData the variant data object we want information from + */ +tetris_highscore_index_t tetris_bastet_getHighscoreIndex(void *pVariantData); + + +void tetris_bastet_setLastInput(void *pVariantData, + tetris_input_command_t inCmd); + + +tetris_orientation_t tetris_bastet_getOrientation(void *pVariantData); + +#endif /* BAST_H_ */ diff --git a/games/tetris/variant_fp.c b/games/tetris/variant_fp.c new file mode 100644 index 0000000..c00e907 --- /dev/null +++ b/games/tetris/variant_fp.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +#include "../../random/prng.h" +#include "../../compat/pgmspace.h" +#include "../../menu/menu.h" +#include "variant_fp.h" +#include "variant_std.h" +#include "variants.h" +#include "tetris_main.h" +#include "piece.h" +#include "playfield.h" +#include "highscore.h" +#include "orientation.h" +#include "input.h" + + +/*************** + * entry point * + ***************/ + +#ifdef MENU_SUPPORT +// First Person Tetris icon, MSB is leftmost pixel +static uint8_t tetrisfp_icon[8] PROGMEM = + { 0xee, 0x89, 0xee, 0x88, 0x88, 0x20, 0x2c, 0x6c }; +game_descriptor_t tetrisfp_game_descriptor + __attribute__((section(".game_descriptors"))) = +{ + &tetris_fp, + tetrisfp_icon, +}; +#endif + + +void tetris_fp(void) +{ + tetris_main(&tetrisFpVariant); +} + + +/**************************** + * construction/destruction * + ****************************/ + +const tetris_variant_t tetrisFpVariant = +{ + &tetris_std_construct, + &tetris_std_destruct, + &tetris_std_choosePiece, + &tetris_std_singleDrop, + &tetris_std_completeDrop, + &tetris_std_removedLines, + &tetris_std_getScore, + &tetris_std_getHighscore, + &tetris_std_setHighscore, + &tetris_std_getHighscoreName, + &tetris_std_setHighscoreName, + &tetris_std_getLevel, + &tetris_std_getLines, + &tetris_std_getPreviewPiece, + &tetris_fp_getHighscoreIndex, + &tetris_fp_setLastInput, + &tetris_std_getOrientation +}; + + +/***************** + * get functions * + *****************/ + +tetris_highscore_index_t tetris_fp_getHighscoreIndex(void *pVariantData) +{ + return TETRIS_HISCORE_FP; +} + + +void tetris_fp_setLastInput(void *pVariantData, + tetris_input_command_t inCmd) +{ + assert (pVariantData != NULL); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + + if (inCmd == TETRIS_INCMD_ROT_CW) + { + pStdVariant->nOrient = (pStdVariant->nOrient + 1) % 4; + } + else if (inCmd == TETRIS_INCMD_ROT_CCW) + { + pStdVariant->nOrient = (pStdVariant->nOrient + 3) % 4; + } +} diff --git a/games/tetris/variant_fp.h b/games/tetris/variant_fp.h new file mode 100644 index 0000000..269cfb2 --- /dev/null +++ b/games/tetris/variant_fp.h @@ -0,0 +1,41 @@ +#ifndef VARIANT_FP_H_ +#define VARIANT_FP_H_ + +#include +#include "variant_std.h" +#include "variants.h" +#include "highscore.h" +#include "piece.h" +#include "orientation.h" +#include "input.h" + + +/*************** + * entry point * + ***************/ + +/** + * runs the First Person Tetris game + */ +void tetris_fp(void); + + +const tetris_variant_t tetrisFpVariant; + + +/********************* + * get/set functions * + *********************/ + + +/** + * retrieves the variant's highscore index + * @param pVariantData the variant data object we want information from + */ +tetris_highscore_index_t tetris_fp_getHighscoreIndex(void *pVariantData); + + +void tetris_fp_setLastInput(void *pVariantData, + tetris_input_command_t inCmd); + +#endif /*VARIANT_FP_H_*/ diff --git a/games/tetris/variant_std.c b/games/tetris/variant_std.c new file mode 100644 index 0000000..2b27fdd --- /dev/null +++ b/games/tetris/variant_std.c @@ -0,0 +1,273 @@ +/* Borgtris + * by: Christian Kroll + * date: Tuesday, 2007/09/16 + */ + +#include +#include +#include +#include + +#include "../../autoconf.h" +#include "../../random/prng.h" +#include "../../compat/pgmspace.h" +#include "../../menu/menu.h" +#include "variant_std.h" +#include "variants.h" +#include "tetris_main.h" +#include "piece.h" +#include "playfield.h" +#include "highscore.h" +#include "orientation.h" +#include "input.h" + + +/*************** + * entry point * + ***************/ + +#ifdef GAME_TETRIS +#ifdef MENU_SUPPORT +// Tetris icon, MSB is leftmost pixel +static uint8_t tetris_icon[8] PROGMEM = + { 0x0f, 0x0f, 0xc3, 0xdb, 0xdb, 0xc3, 0xf0, 0xf0 }; +game_descriptor_t tetris_game_descriptor + __attribute__((section(".game_descriptors"))) = +{ + &tetris, + tetris_icon, +}; +#endif + + +void tetris(void) +{ + tetris_main(&tetrisStdVariant); +} +#endif + +/**************************** + * construction/destruction * + ****************************/ + +#ifdef GAME_TETRIS +const tetris_variant_t tetrisStdVariant = +{ + &tetris_std_construct, + &tetris_std_destruct, + &tetris_std_choosePiece, + &tetris_std_singleDrop, + &tetris_std_completeDrop, + &tetris_std_removedLines, + &tetris_std_getScore, + &tetris_std_getHighscore, + &tetris_std_setHighscore, + &tetris_std_getHighscoreName, + &tetris_std_setHighscoreName, + &tetris_std_getLevel, + &tetris_std_getLines, + &tetris_std_getPreviewPiece, + &tetris_std_getHighscoreIndex, + &tetris_std_setLastInput, + &tetris_std_getOrientation +}; +#endif + + +void *tetris_std_construct(tetris_playfield_t *pPl) +{ + tetris_standard_variant_t *pStdVariant = (tetris_standard_variant_t *) + malloc(sizeof(tetris_standard_variant_t)); + assert(pStdVariant != NULL); + memset(pStdVariant, 0, sizeof(tetris_standard_variant_t)); + + return pStdVariant; +} + + +void tetris_std_destruct(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + if (pStdVariant->pPreviewPiece != NULL) + { + tetris_piece_destruct(pStdVariant->pPreviewPiece); + } + free(pStdVariant); +} + + +/***************************** + * variant related functions * + *****************************/ + + +tetris_piece_t* tetris_std_choosePiece(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + if (pStdVariant->pPreviewPiece == NULL) + { + pStdVariant->pPreviewPiece = + tetris_piece_construct(random8() % 7, TETRIS_PC_ANGLE_0); + return tetris_piece_construct(random8() % 7, TETRIS_PC_ANGLE_0); + } + else + { + tetris_piece_t *pPiece = pStdVariant->pPreviewPiece; + pStdVariant->pPreviewPiece = + tetris_piece_construct(random8() % 7, TETRIS_PC_ANGLE_0); + return pPiece; + } +} + + +void tetris_std_singleDrop(void *pVariantData, + uint8_t nLines) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + pStdVariant->nScore += nLines; +} + + +void tetris_std_completeDrop(void *pVariantData, + uint8_t nLines) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + pStdVariant->nScore += nLines * 2; +} + + +void tetris_std_removedLines(void *pVariantData, + uint8_t nRowMask) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + uint8_t nLines = tetris_playfield_calculateLines(nRowMask); + pStdVariant->nLines += nLines; + pStdVariant->nLevel = ((pStdVariant->nLines / 10) < TETRIS_INPUT_LEVELS) ? + (pStdVariant->nLines / 10) : (TETRIS_INPUT_LEVELS - 1); + + switch (nLines) + { + case 1: + pStdVariant->nScore += 50; + break; + case 2: + pStdVariant->nScore += 150; + break; + case 3: + pStdVariant->nScore += 250; + break; + case 4: + pStdVariant->nScore += 400; + break; + } +} + + +/***************** + * get functions * + *****************/ + +uint16_t tetris_std_getScore(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + return pStdVariant->nScore; +} + + +uint16_t tetris_std_getHighscore(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + return pStdVariant->nHighscore; +} + + +void tetris_std_setHighscore(void *pVariantData, + uint16_t nHighscore) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + pStdVariant->nHighscore = nHighscore; +} + + +uint16_t tetris_std_getHighscoreName(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + return pStdVariant->nHighscoreName; +} + + +void tetris_std_setHighscoreName(void *pVariantData, + uint16_t nHighscoreName) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + pStdVariant->nHighscoreName = nHighscoreName; +} + + +uint8_t tetris_std_getLevel(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + return pStdVariant->nLevel; +} + + +uint16_t tetris_std_getLines(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + return pStdVariant->nLines; +} + + +tetris_piece_t* tetris_std_getPreviewPiece(void *pVariantData) +{ + assert(pVariantData != 0); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + return pStdVariant->pPreviewPiece; +} + + +tetris_highscore_index_t tetris_std_getHighscoreIndex(void *pVariantData) +{ + return TETRIS_HISCORE_TETRIS; +} + + +void tetris_std_setLastInput(void *pVariantData, + tetris_input_command_t inCmd) +{ +} + + +tetris_orientation_t tetris_std_getOrientation(void *pVariantData) +{ + assert (pVariantData != NULL); + tetris_standard_variant_t *pStdVariant = + (tetris_standard_variant_t *)pVariantData; + + return pStdVariant->nOrient; +} diff --git a/games/tetris/variant_std.h b/games/tetris/variant_std.h new file mode 100644 index 0000000..6609080 --- /dev/null +++ b/games/tetris/variant_std.h @@ -0,0 +1,186 @@ +#ifndef VARIANT_STD_H_ +#define VARIANT_STD_H_ + +#include +#include "../../autoconf.h" +#include "variants.h" +#include "piece.h" +#include "orientation.h" +#include "input.h" + + +/*************** + * entry point * + ***************/ + +#ifdef GAME_TETRIS +/** + * runs the tetris game + */ +void tetris(void); + +#endif + + +/********* + * types * + *********/ + +typedef struct tetris_standard_variant_t +{ + uint16_t nScore; /** score of the player */ + uint16_t nHighscore; /** highscore */ + uint16_t nHighscoreName; /** champion's initials */ + uint8_t nLevel; /** current level */ + uint16_t nLines; /** number of completed lines */ + tetris_piece_t *pPreviewPiece; /** the piece intended to be the next one */ + tetris_orientation_t nOrient; /** desired orientation of the playfield */ +} +tetris_standard_variant_t; + + +const tetris_variant_t tetrisStdVariant; + + +/**************************** + * construction/destruction * + ****************************/ + +/** + * constructs a variant data object + * @param pPl related playfield object + * @return pointer to a newly created variant data object + */ +void *tetris_std_construct(tetris_playfield_t *pPl); + + +/** + * destructs a variant data object + * @param pVariantData pointer to a variant data object to be destructed + */ +void tetris_std_destruct(void *pVariantData); + + +/***************************** + * variant related functions * + *****************************/ + +/** + * chooses a new piece + * @param pVariantData the variant instance of interest + * @return a tetris piece + */ +tetris_piece_t* tetris_std_choosePiece(void *pVariantData); + + +/** + * add points which result from single step dropping + * @param pVariantData the variant data object we want to modify + * @param nLines the number of rows involved + */ +void tetris_std_singleDrop(void *pVariantData, + uint8_t nLines); + + +/** + * add points which result from a complete drop + * @param pVariantData the variant data object we want to modify + * @param nLines the number of rows involved + */ +void tetris_std_completeDrop(void *pVariantData, + uint8_t nLines); + + +/** + * add points which result from removed rows + * @param pVariantData the variant data object we want to modify + * @param nRowMask bit mask of removed lines + */ +void tetris_std_removedLines(void *pVariantData, + uint8_t nRowMask); + + +/********************* + * get/set functions * + *********************/ + +/** + * returns the current score + * @param pVariantData variant data object we want information from + * @return score + */ +uint16_t tetris_std_getScore(void *pVariantData); + + +/** + * returns the current highscore + * @param pVariantData variant data object we want information from + * @return highscore + */ +uint16_t tetris_std_getHighscore(void *pVariantData); + + +/** + * set highscore + * @param pVariantData variant data object we want to modify + * @param nHighscore highscore + */ +void tetris_std_setHighscore(void *pVariantData, + uint16_t nHighscore); + + +/** + * returns the current highscore name + * @param pVariantData variant data object we want information from + * @return champion's name packed as uint16_t + */ +uint16_t tetris_std_getHighscoreName(void *pVariantData); + + +/** + * set highscore name + * @param pVariantData the variant data object we want to modify + * @param nHighscoreName champion's name packed as uint16_t + */ +void tetris_std_setHighscoreName(void *pVariantData, + uint16_t nHighscoreName); + + +/** + * returns the current level + * @param pVariantData variant data object we want information from + * @return the level as uint8_t + */ +uint8_t tetris_std_getLevel(void *pVariantData); + + +/** + * returns the number of completed lines + * @param pVariantData the variant data object we want information from + * @return number of completed lines + */ +uint16_t tetris_std_getLines(void *pVariantData); + + +/** + * returns piece which was set via tetris_std_setPreviewPiece + * @param pVariantData the variant data object we want information from + * @return the piece intended to be the next one + */ +tetris_piece_t* tetris_std_getPreviewPiece(void *pVariantData); + + +/** + * retrieves the variant's highscore index + * @param pVariantData the variant data object we want information from + */ +tetris_highscore_index_t tetris_std_getHighscoreIndex(void *pVariantData); + + +void tetris_std_setLastInput(void *pVariantData, + tetris_input_command_t inCmd); + + +tetris_orientation_t tetris_std_getOrientation(void *pVariantData); + +#endif /*VARIANT_STD_H_*/ diff --git a/games/tetris/variants.h b/games/tetris/variants.h new file mode 100644 index 0000000..da68d16 --- /dev/null +++ b/games/tetris/variants.h @@ -0,0 +1,142 @@ +#ifndef VARIANTS_H_ +#define VARIANTS_H_ + +#include +#include "playfield.h" +#include "piece.h" +#include "highscore.h" +#include "orientation.h" +#include "input.h" + +typedef struct tetris_variant_t +{ + /** + * constructs a variant data object + * @param pPl related playfield object + * @return pointer to a newly created variant data object + */ + void* (*construct)(tetris_playfield_t *pPl); + + + /** + * destructs a variant data object + * @param pVariantData pointer to a logic object to be destructed + */ + void (*destruct)(void *pVariantData); + + + /** + * chooses a new piece + * @param pVariantData the variant instance of interest + * @return a tetris piece + */ + tetris_piece_t* (*choosePiece)(void *pVariantData); + + + /** + * add points which result from single step dropping + * @param pVariantData the variant data object we want to modify + * @param nLines the number of rows involved + */ + void (*singleDrop)(void *pVariantData, + uint8_t nLines); + + + /** + * add points which result from a complete drop + * @param pVariantData the variant data object we want to modify + * @param nLines the number of rows involved + */ + void (*completeDrop)(void *pVariantData, + uint8_t nLines); + + + /** + * add points which result from removed rows + * @param pVariantData the variant data object we want to modify + * @param nRowMask bit mask of removed lines + */ + void (*removedLines)(void *pVariantData, + uint8_t nRowMask); + + + /** + * returns the current score + * @param pVariantData variant data object we want information from + * @return score + */ + uint16_t (*getScore)(void *pVariantData); + + + /** + * returns the current highscore + * @param pVariantData variant data object we want information from + * @return highscore + */ + uint16_t (*getHighscore)(void *pVariantData); + + + /** + * set highscore + * @param pVariantData variant data object we want to modify + * @param nHighscore highscore + */ + void (*setHighscore)(void *pVariantData, + uint16_t nHighscore); + + + /** + * returns the current highscore name + * @param pVariantData variant data object we want information from + * @return champion's name packed as uint16_t + */ + uint16_t (*getHighscoreName)(void *pVariantData); + + + /** + * set highscore name + * @param pVariantData the variant data object we want to modify + * @param nHighscoreName champion's name packed as uint16_t + */ + void (*setHighscoreName)(void *pVariantData, + uint16_t nHighscoreName); + + + /** + * returns the current level + * @param pVariantData variant data object we want information from + * @return the level as uint8_t + */ + uint8_t (*getLevel)(void *pVariantData); + + + /** + * returns the number of completed lines + * @param pVariantData the variant data object we want information from + * @return number of completed lines + */ + uint16_t (*getLines)(void *pVariantData); + + + /** + * returns piece which was set via tetris_std_setPreviewPiece + * @param pVariantData the variant data object we want information from + * @return the piece intended to be the next one + */ + tetris_piece_t* (*getPreviewPiece)(void *pVariantData); + + + /** + * retrieves the variant's highscore index + * @param pVariantData the variant data object we want information from + */ + tetris_highscore_index_t (*getHighscoreIndex)(void *pVariantData); + + void (*setLastInput)(void *pVariantData, + tetris_input_command_t inCmd); + + tetris_orientation_t (*getOrientation)(void *pVariantData); +} +tetris_variant_t; + +#endif /* VARIANTS_H_ */ diff --git a/games/tetris/view.c b/games/tetris/view.c index 89a8a15..5a098f0 100644 --- a/games/tetris/view.c +++ b/games/tetris/view.c @@ -3,60 +3,202 @@ #include #include #include -#include "../../config.h" +#include "../../autoconf.h" #include "../../pixel.h" #include "../../util.h" #include "../../scrolltext/scrolltext.h" -#include "logic.h" +#include "variants.h" #include "piece.h" #include "playfield.h" #include "view.h" #define WAIT(ms) wait(ms) -#ifdef GAME_TETRIS_FP -uint8_t tetris_screendir; -#endif - -void (*setpixel_wrapper)(pixel p, unsigned char value); - +/** + * \defgroup TetrisViewDefinesPrivate View: Internal constants + */ +/*@{*/ /*********** * defines * ***********/ -// how often should the border blink (to indicate level up) +/** 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 +/** 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 +/** 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 +/** amount of time (in ms) between line color changes */ #define TETRIS_VIEW_LINE_BLINK_DELAY 75 -// colors of game elements +/** 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 * ***************************/ -/* Function: tetris_view_getPieceColor - * Description: helper function to dim the piece color if game is paused - * Argument pV: pointer to the view whose pause status is of interest - * Return value: void +/** + * setpixel replacement which may transform the pixel coordinates + * @param pV pointer to the view we want to draw on + * @param x x-coordinate of the pixel + * @param y y-coordinate of the pixel + * @param nColor Color of the pixel */ -uint8_t tetris_view_getPieceColor (tetris_view_t *pV) +void tetris_view_setpixel(tetris_orientation_t nOrientation, + uint8_t x, + uint8_t y, + uint8_t nColor) +{ + x = VIEWCOLS - 1 - x; + + switch (nOrientation) + { + case TETRIS_ORIENTATION_0: + setpixel((pixel){x, y}, nColor); + break; + case TETRIS_ORIENTATION_90: + setpixel((pixel){y, VIEWCOLS - 1 - x}, nColor); + break; + case TETRIS_ORIENTATION_180: + setpixel((pixel){VIEWCOLS - 1 - x, VIEWROWS - 1 - y}, nColor); + break; + case TETRIS_ORIENTATION_270: + setpixel((pixel){VIEWROWS - 1 - y, x}, nColor); + break; + } +} + + +/** + * draws a horizontal line + * @param nOrient orientation 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_orientation_t nOrient, + 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(nOrient, x, y, nColor); + } +} + + +/** + * draws a vertical line + * @param nOrient orientation 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_orientation_t nOrient, + 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(nOrient, 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) { @@ -69,10 +211,9 @@ uint8_t tetris_view_getPieceColor (tetris_view_t *pV) } -/* Function: tetris_view_drawDump - * Description: redraws the dump and the falling piece (if necessary) - * Argument pV: pointer to the view on which the dump should be drawn - * Return value: void +/** + * 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) { @@ -82,24 +223,15 @@ void tetris_view_drawDump(tetris_view_t *pV) return; } + tetris_orientation_t nOrient = + pV->pVariantMethods->getOrientation(pV->pVariant); + int8_t nPieceRow = tetris_playfield_getRow(pV->pPl); - - // only redraw dump completely if the view mode has been changed - int8_t nStartRow; - if (pV->modeCurrent == pV->modeOld) - { - nStartRow = ((nPieceRow + 3) < 16) ? (nPieceRow + 3) : 15; - } - else - { - nStartRow = 15; - } - uint16_t nRowMap; uint16_t nElementMask; tetris_playfield_status_t status = tetris_playfield_getStatus(pV->pPl); - for (int8_t nRow = nStartRow; nRow >= 0; --nRow) + for (int8_t nRow = TETRIS_VIEW_HEIGHT_DUMP - 1; nRow >= 0; --nRow) { nRowMap = tetris_playfield_getDumpRow(pV->pPl, nRow); @@ -126,7 +258,7 @@ void tetris_view_drawDump(tetris_view_t *pV) nPieceMap >>= -nColumn; } // cut off unwanted stuff - nPieceMap &= 0x03ff; + // nPieceMap &= 0x03ff; // finally embed piece into the view nRowMap |= nPieceMap; } @@ -134,7 +266,7 @@ void tetris_view_drawDump(tetris_view_t *pV) nElementMask = 0x0001; - for (int8_t x = 0; x < 10; ++x) + for (int8_t x = 0; x < TETRIS_VIEW_WIDTH_DUMP; ++x) { unsigned char nColor; if ((nRowMap & nElementMask) != 0) @@ -145,21 +277,24 @@ void tetris_view_drawDump(tetris_view_t *pV) { nColor = TETRIS_VIEW_COLORSPACE; } - setpixel_wrapper((pixel){14-x,nRow}, nColor); + tetris_view_setpixel(nOrient, TETRIS_VIEW_XOFFSET_DUMP + x, + TETRIS_VIEW_YOFFSET_DUMP + nRow, nColor); nElementMask <<= 1; } } } - -/* Function: tetris_view_drawPreviewPiece - * Description: redraws the preview window - * Argument pV: pointer to the view on which the piece should be drawn - * Argmument pPc: pointer to the piece for the preview window (may be NULL) - * Return value: void +#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_orientation_t nOrient = + pV->pVariantMethods->getOrientation(pV->pVariant); + if (pPc != NULL) { uint8_t nColor; @@ -171,6 +306,7 @@ void tetris_view_drawPreviewPiece(tetris_view_t *pV, tetris_piece_t *pPc) } else { + // an iconized "P" nPieceMap = 0x26a6; } @@ -186,7 +322,10 @@ void tetris_view_drawPreviewPiece(tetris_view_t *pV, tetris_piece_t *pPc) { nColor = TETRIS_VIEW_COLORSPACE; } - setpixel_wrapper((pixel) {3 - x, y + 6}, nColor); + tetris_view_setpixel(nOrient, + TETRIS_VIEW_XOFFSET_PREVIEW + x, + TETRIS_VIEW_YOFFSET_PREVIEW + y, + nColor); nElementMask <<= 1; } } @@ -197,74 +336,153 @@ void tetris_view_drawPreviewPiece(tetris_view_t *pV, tetris_piece_t *pPc) { for (uint8_t x = 0; x < 4; ++x) { - setpixel_wrapper((pixel) {3 - x, y + 6}, TETRIS_VIEW_COLORSPACE); + tetris_view_setpixel(nOrient, + TETRIS_VIEW_XOFFSET_PREVIEW + x, + TETRIS_VIEW_YOFFSET_PREVIEW + y, + TETRIS_VIEW_COLORSPACE); } } } } +#endif - -/* Function: tetris_view_drawBorders - * Description: draws borders in the given color - * Argument nColor: the color for the border - * Return value: void +/** + * 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(uint8_t nColor) +void tetris_view_drawBorders(tetris_view_t *pV, + uint8_t nColor) { - // drawing playfield - uint8_t x, y; - for (y = 0; y < 16; ++y) + tetris_orientation_t nOrient = + pV->pVariantMethods->getOrientation(pV->pVariant); + +#if TETRIS_VIEW_YOFFSET_DUMP != 0 + // fill upper space if required + for (uint8_t y = 0; y < TETRIS_VIEW_YOFFSET_DUMP; ++y) { - setpixel_wrapper((pixel){4, y}, nColor); - setpixel_wrapper((pixel){15, y}, nColor); + tetris_view_drawHLine(nOrient, 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(nOrient, 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(nOrient, 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(nOrient, x, TETRIS_VIEW_YOFFSET_DUMP, + TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor); + } +#endif + + +#ifdef TETRIS_VIEW_XOFFSET_COUNTER + tetris_view_drawVLine(nOrient, 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(nOrient, x, TETRIS_VIEW_YOFFSET_DUMP, + TETRIS_VIEW_YOFFSET_COUNT100 - 1, nColor); + tetris_view_drawVLine(nOrient, x, TETRIS_VIEW_YOFFSET_PREVIEW + 4, + TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor); } - for (y = 0; y < 5; ++y) + tetris_view_drawVLine(nOrient, TETRIS_VIEW_XOFFSET_COUNTER + 3, + TETRIS_VIEW_YOFFSET_DUMP, TETRIS_VIEW_YOFFSET_COUNT1 + 3, nColor); + + tetris_view_drawVLine(nOrient, TETRIS_VIEW_XOFFSET_COUNTER + 3, + TETRIS_VIEW_YOFFSET_PREVIEW + 4, + TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor); + + tetris_view_drawHLine(nOrient, TETRIS_VIEW_XOFFSET_COUNTER, + TETRIS_VIEW_XOFFSET_COUNTER + 3, TETRIS_VIEW_YOFFSET_COUNT100 + 1, + nColor); + + tetris_view_drawHLine(nOrient, TETRIS_VIEW_XOFFSET_COUNTER, + TETRIS_VIEW_XOFFSET_COUNTER + 3, TETRIS_VIEW_YOFFSET_COUNT10 + 3, + nColor); + + tetris_view_drawHLine(nOrient, TETRIS_VIEW_XOFFSET_COUNTER, + TETRIS_VIEW_XOFFSET_COUNTER + 3, TETRIS_VIEW_YOFFSET_COUNT1 + 3, + nColor); +#elif defined TETRIS_VIEW_XOFFSET_PREVIEW + tetris_view_drawVLine(nOrient, 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) { - for (x = 0; x <= 3; ++x) - { - if ((y < 1 || y > 3) || (x < 1 || y > 3)) - { - setpixel_wrapper((pixel){x, y}, nColor); - setpixel_wrapper((pixel){x, y + 11}, nColor); - } - } + tetris_view_drawVLine(nOrient, x, TETRIS_VIEW_YOFFSET_DUMP, + TETRIS_VIEW_YOFFSET_PREVIEW - 1, nColor); + tetris_view_drawVLine(nOrient, 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(nOrient, x, TETRIS_VIEW_YOFFSET_DUMP, + TETRIS_VIEW_YOFFSET_DUMP + TETRIS_VIEW_HEIGHT_DUMP - 1, nColor); + } +#endif } -/* Function: tetris_view_blinkBorders - * Description: lets the borders blink to notify player of a level change - * Return value: void +/** + * 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() +void tetris_view_blinkBorders(tetris_view_t *pV) { for (uint8_t i = 0; i < TETRIS_VIEW_BORDER_BLINK_COUNT; ++i) { - tetris_view_drawBorders(TETRIS_VIEW_COLORPIECE); + tetris_view_drawBorders(pV, TETRIS_VIEW_COLORPIECE); WAIT(TETRIS_VIEW_BORDER_BLINK_DELAY); - tetris_view_drawBorders(TETRIS_VIEW_COLORBORDER); + tetris_view_drawBorders(pV, TETRIS_VIEW_COLORBORDER); WAIT(TETRIS_VIEW_BORDER_BLINK_DELAY); } } -/* Function: tetris_view_blinkLines - * Description: lets complete lines blink to emphasize their removal - * Argmument pPl: pointer to the playfield whose complete lines should blink - * Return value: void +/** + * lets complete lines blink to emphasize their removal + * @param pPl pointer to the view whose complete lines should blink */ - - -void tetris_view_blinkLines(tetris_playfield_t *pPl) +void tetris_view_blinkLines(tetris_view_t *pV) { + // reduce necessity of pointer arithmetic - int8_t nRow = tetris_playfield_getRow(pPl); - uint8_t nRowMask = tetris_playfield_getRowMask(pPl); + int8_t nRow = tetris_playfield_getRow(pV->pPl); + uint8_t nRowMask = tetris_playfield_getRowMask(pV->pPl); + + tetris_orientation_t nOrient = + pV->pVariantMethods->getOrientation(pV->pVariant); // don't try to draw below the border - int8_t nDeepestRowOffset = ((nRow + 3) < tetris_playfield_getHeight(pPl) ? - 3 : tetris_playfield_getHeight(pPl) - (nRow + 1)); + 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) @@ -285,7 +503,11 @@ void tetris_view_blinkLines(tetris_playfield_t *pPl) uint8_t nColor = (nColIdx == 0 ? TETRIS_VIEW_COLORFADE : TETRIS_VIEW_COLORPIECE); - setpixel_wrapper((pixel){14 - x, y}, nColor); + // setpixel((pixel){14 - x, y}, nColor); + tetris_view_setpixel(nOrient, + TETRIS_VIEW_XOFFSET_DUMP + x, + TETRIS_VIEW_YOFFSET_DUMP + y, + nColor); } } } @@ -296,92 +518,122 @@ void tetris_view_blinkLines(tetris_playfield_t *pPl) } -/* Function: tetris_view_showLineNumbers - * Description: displays completed Lines (0-99) - * Argmument pV: pointer to the view - * Return value: void +#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_orientation_t nOrient = + pV->pVariantMethods->getOrientation(pV->pVariant); + // get number of completed lines - uint16_t nLines = tetris_logic_getLines(pV->pLogic); + 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 = 1, y = 1; i < 9; ++i) + for (int i = 0, x = 0, y = 0; i < 9; ++i) { - // pick drawing color + // pick drawing color for the ones uint8_t nOnesPen = nOnes > i ? TETRIS_VIEW_COLORCOUNTER : TETRIS_VIEW_COLORSPACE; + tetris_view_setpixel(nOrient, 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; - // wrap lines if required - if ((x % 4) == 0) + tetris_view_setpixel(nOrient, TETRIS_VIEW_XOFFSET_COUNTER + x, + TETRIS_VIEW_YOFFSET_COUNT10 + y, nTensPen); + + // a maximum of 399 lines can be displayed + if (i < 3) { - y++; - x = 1; + // pick drawing color for the hundreds + uint8_t nHundredsPen = nHundreds > i ? + TETRIS_VIEW_COLORCOUNTER : TETRIS_VIEW_COLORSPACE; + tetris_view_setpixel(nOrient, TETRIS_VIEW_XOFFSET_COUNTER + x, + TETRIS_VIEW_YOFFSET_COUNT100 + y, nHundredsPen); + + } + + // wrap lines if required + if ((++x % 3) == 0) + { + ++y; + x = 0; } - // ones - setpixel_wrapper((pixel){x, y}, nOnesPen); - // tens (increment x, add vertical offset for lower part of the border) - setpixel_wrapper((pixel){x++, y + 11}, nTensPen); } } +#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 * ****************************/ -/* Function: tetris_view_construct - * Description: constructs a view for André's borg - * Argument pPl: pointer to logic object which should be observed - * Argument pPl: pointer to playfield which should be observed - * Return value: pointer to a newly created view - */ -tetris_view_t *tetris_view_construct(tetris_logic_t *pLogic, - tetris_playfield_t *pPl, - uint8_t nFirstPerson) +tetris_view_t *tetris_view_construct(const tetris_variant_t *const pVarMethods, + void *pVariantData, + tetris_playfield_t *pPl) { // memory allocation - assert((pLogic != NULL) && (pPl != NULL)); + assert((pVariantData != NULL) && (pPl != 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->pLogic = pLogic; + pView->pVariantMethods = pVarMethods; + pView->pVariant = pVariantData; pView->pPl = pPl; - pView->modeCurrent = TETRIS_VIMO_RUNNING; - pView->modeOld = TETRIS_VIMO_RUNNING; - -#ifdef GAME_TETRIS_FP - // set setpixel wrapper - if (nFirstPerson) - setpixel_wrapper = tetris_view_setpixel_fp; - else - setpixel_wrapper = setpixel; -#else - setpixel_wrapper = setpixel; -#endif + pView->modeCurrent = pView->modeOld = TETRIS_VIMO_RUNNING; // drawing some first stuff clear_screen(0); - tetris_view_drawBorders(TETRIS_VIEW_COLORBORDER); + tetris_view_drawBorders(pView, TETRIS_VIEW_COLORBORDER); return pView; } -/* Function: tetris_view_destruct - * Description: destructs a view - * Argument pView: pointer to the view which should be destructed - * Return value: void - */ void tetris_view_destruct(tetris_view_t *pView) { assert(pView != NULL); @@ -393,27 +645,15 @@ void tetris_view_destruct(tetris_view_t *pView) * view related functions * ***************************/ -/* Function: tetris_view_getDimensions - * Description: destructs a view - * Argument w: [out] pointer to an int8_t to store the playfield width - * Argument h: [out] pointer to an int8_t to store the playfield height - * Return value: void - */ void tetris_view_getDimensions(int8_t *w, int8_t *h) { assert((w != NULL) && (h != NULL)); - *w = 10; - *h = 16; + *w = TETRIS_VIEW_WIDTH_DUMP; + *h = TETRIS_VIEW_HEIGHT_DUMP; } -/* Function: tetris_view_setViewMode - * Description: sets the view mode (pause or running) - * Argument pV: pointer to the view whose mode should be set - * Argument vm: see definition of tetris_view_mode_t - * Return value: void - */ void tetris_view_setViewMode(tetris_view_t *pV, tetris_view_mode_t vm) { pV->modeOld = pV->modeCurrent; @@ -421,75 +661,52 @@ void tetris_view_setViewMode(tetris_view_t *pV, tetris_view_mode_t vm) } -/* Function: tetris_view_update - * Description: informs a view about changes in the game - * Argument pV: pointer to the view which should be updated - * Return value: void - */ 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_playfield_getRowMask(pV->pPl) != 0) { - tetris_view_blinkLines(pV->pPl); - tetris_view_showLineNumbers(pV); + tetris_view_blinkLines(pV); } - // draw preview piece - tetris_view_drawPreviewPiece(pV, tetris_logic_getPreviewPiece(pV->pLogic)); +#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 = tetris_logic_getLevel(pV->pLogic); + uint8_t nLevel = pV->pVariantMethods->getLevel(pV->pVariant); if (nLevel != pV->nOldLevel) { - tetris_view_blinkBorders(); + tetris_view_blinkBorders(pV); pV->nOldLevel = nLevel; } } -/* Function: tetris_view_formatHighscoreName - * Description: convert uint16_t into ascii Highscore - * (only used internally in view.c) - * Argument nHighscoreName: packed integer with highscoreName - * Argument pszName: pointer to a char array where result is stored - * Return value: void - */ -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; -} - - -/* Function: tetris_view_showResults - * Description: shows results after game - * Argument pV: pointer to the view which should show the reults - * Return value: void - */ void tetris_view_showResults(tetris_view_t *pV) { #ifdef SCROLLTEXT_SUPPORT char pszResults[54], pszHighscoreName[4]; - uint16_t nScore = tetris_logic_getScore(pV->pLogic); - uint16_t nHighscore = tetris_logic_getHighscore(pV->pLogic); - uint16_t nLines = tetris_logic_getLines(pV->pLogic); - - uint16_t nHighscoreName = tetris_logic_getHighscoreName(pV->pLogic); + 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) @@ -506,60 +723,3 @@ void tetris_view_showResults(tetris_view_t *pV) scrolltext(pszResults); #endif } - - -#ifdef GAME_TETRIS_FP -/* Function: tetris_view_setpixel_fp - * Description: own setpixel wrapper for first person mode - * Return value: void - */ -void tetris_view_setpixel_fp(pixel p, unsigned char value) { - switch (tetris_screendir) { - case 0: setpixel(p,value); break; - case 1: setpixel((pixel){p.y,15-p.x}, value); break; - case 2: setpixel((pixel){15-p.x,15-p.y}, value); break; - case 3: setpixel((pixel){15-p.y,p.x}, value); break; - } -} - -/* Function: tetris_view_rotate - * Description: rotate view for first person mode - * Return value: void - */ -void tetris_view_rotate(void) { - unsigned char plane, row, byte, shift, off, sbyte, hrow; - unsigned char new_pixmap[NUMPLANE][NUM_ROWS][LINEBYTES]; - - tetris_screendir = (tetris_screendir+1)%4; - -// if ( NUM_ROWS != 16 || LINEBYTES != 2 ) return; - - memset(&new_pixmap, 0, sizeof(new_pixmap)); - for(plane=0; plane> shift)&1) << 7 ) | - ( ((pixmap[plane][off-1][sbyte] >> shift)&1) << 6 ) | - ( ((pixmap[plane][off-2][sbyte] >> shift)&1) << 5 ) | - ( ((pixmap[plane][off-3][sbyte] >> shift)&1) << 4 ) | - ( ((pixmap[plane][off-4][sbyte] >> shift)&1) << 3 ) | - ( ((pixmap[plane][off-5][sbyte] >> shift)&1) << 2 ) | - ( ((pixmap[plane][off-6][sbyte] >> shift)&1) << 1 ) | - ( ((pixmap[plane][off-7][sbyte] >> shift)&1) << 0 ) - ); - - } - } - } - - memcpy(&pixmap, &new_pixmap, sizeof(pixmap)); -} - -#endif \ No newline at end of file diff --git a/games/tetris/view.h b/games/tetris/view.h index 2872d02..e4455e2 100644 --- a/games/tetris/view.h +++ b/games/tetris/view.h @@ -2,16 +2,21 @@ #define TETRIS_VIEW_H_ #include -#include "logic.h" +#include "variants.h" #include "piece.h" #include "playfield.h" +/** + * \defgroup TetrisViewTypes View: Data types + */ +/*@{*/ + /********* * types * *********/ -// presentation modes +/** presentation modes */ typedef enum tetris_view_mode_t { TETRIS_VIMO_PAUSED, @@ -19,37 +24,47 @@ typedef enum tetris_view_mode_t } tetris_view_mode_t; + +/** data structure that drives the view module */ typedef struct tetris_view_t { - tetris_logic_t *pLogic; // associated logic object - tetris_playfield_t *pPl; // associated playfield - tetris_view_mode_t modeCurrent; // current presentation mode - tetris_view_mode_t modeOld; // old presentation mode - uint8_t nOldLevel; // helper variable to recognize level changes - + const tetris_variant_t *pVariantMethods; /** variant function pointers */ + void *pVariant; /** associated variant object */ + tetris_playfield_t *pPl; /** associated playfield */ + tetris_view_mode_t modeCurrent; /** current presentation mode */ + tetris_view_mode_t modeOld; /** old presentation mode */ + uint8_t nOldLevel; /** for detecting level changes */ + tetris_orientation_t nOrient; /** orientation for the playfield */ } tetris_view_t; +/*@}*/ + + +/** + * \defgroup TetrisInterface View: Interface functions + */ +/*@{*/ /***************************** * construction/destruction * *****************************/ -/* Function: tetris_view_construct - * Description: constructs a view for André's borg - * Argument pPl: pointer to logic object which should be observed - * Argument pPl: pointer to playfield which should be observed - * Return value: pointer to a newly created view +/** + * constructs a view for André's borg + * @param pVarMethods associated variant method pointers + * @param pVariantData pointer to variant data object which should be observed + * @param pPl pointer to playfield which should be observed + * @return pointer to a newly created view */ -tetris_view_t *tetris_view_construct(tetris_logic_t *pLogic, - tetris_playfield_t *pPl, - uint8_t nFirstPerson); +tetris_view_t *tetris_view_construct(const tetris_variant_t *const pVarMethods, + void *pVariantData, + tetris_playfield_t *pPl); -/* Function: tetris_view_destruct - * Description: destructs a view - * Argument pView: pointer to the view to be destructed - * Return value: void +/** + * destructs a view + * @param pView: pointer to the view to be destructed */ void tetris_view_destruct(tetris_view_t *pView); @@ -58,47 +73,37 @@ void tetris_view_destruct(tetris_view_t *pView); * view related functions * ***************************/ -/* Function: tetris_view_getDimensions - * Description: destructs a view - * Argument w: [out] pointer to an int8_t to store the playfield width - * Argument h: [out] pointer to an int8_t to store the playfield height - * Return value: void +/** + * destructs a view + * @param w pointer to an int8_t to store the playfield width + * @param h pointer to an int8_t to store the playfield height */ void tetris_view_getDimensions(int8_t *w, int8_t *h); -/* Function: tetris_view_setViewMode - * Description: sets the view mode (pause or running) - * Argument pV: pointer to the view whose mode should be set - * Argument vm: see definition of tetris_view_mode_t - * Return value: void +/** + * sets the view mode (pause or running) + * @param pV pointer to the view whose mode should be set + * @param vm see definition of tetris_view_mode_t */ void tetris_view_setViewMode(tetris_view_t *pV, tetris_view_mode_t vm); -/* Function: tetris_view_update - * Description: informs a view about changes in the game - * Argument pV: pointer to the view which should be updated - * Return value: void +/** + * informs a view about changes in the game + * @param pV pointer to the view which should be updated */ void tetris_view_update(tetris_view_t *pV); -/* Function: tetris_view_showResults - * Description: shows results after game - * Argument pV: pointer to the view which should show the reults - * Return value: void +/** + * shows results after game + * @param pV pointer to the view which should show the results */ void tetris_view_showResults(tetris_view_t *pV); -#ifdef GAME_TETRIS_FP -/* Function: tetris_view_setpixel_fp - * Description: own setpixel wrapper for first person mode - * Return value: void - */ -void tetris_view_setpixel_fp(pixel p, unsigned char value); -#endif #endif /*TETRIS_VIEW_H_*/ +/*@}*/