/* Start a new game */ static void startGame(StcGame *game) { int i; /* Initialize game data */ game->errorCode = GAME_ERROR_NONE; game->systemTime = platformGetSystemTime(); game->lastFallTime = game->systemTime; game->isOver = 0; game->isPaused = 0; game->showPreview = 1; game->events = EVENT_NONE; game->delay = INI_DELAY_FALL; /* Initialize game statistics */ game->stats.score = 0; game->stats.lines = 0; game->stats.totalPieces = 0; game->stats.level = 0; for (i = 0; i < 7; ++i) { game->stats.pieces[i] = 0; } /* Initialize rand generator */ srand(game->systemTime); /* Initialize game tile map */ setMatrixCells(&game->map[0][0], BOARD_WIDTH, BOARD_HEIGHT, EMPTY_CELL); /* Initialize falling tetromino */ setTetramino(rand() % 7, &game->fallingBlock); game->fallingBlock.x = (BOARD_WIDTH - game->fallingBlock.size) / 2; game->fallingBlock.y = 0; /* Initialize preview tetromino */ setTetramino(rand() % 7, &game->nextBlock); }
// Initialize tetromino cells for every type of tetromino void Game::setTetromino(int indexTetromino, StcTetromino *tetromino) { // Initialize tetromino cells to empty cells setMatrixCells(&tetromino->cells[0][0], TETROMINO_SIZE, TETROMINO_SIZE, EMPTY_CELL); // Almost all the blocks have size 3 tetromino->size = TETROMINO_SIZE - 1; // Initial configuration from: http://tetris.wikia.com/wiki/SRS switch (indexTetromino) { case TETROMINO_I: tetromino->cells[0][1] = COLOR_CYAN; tetromino->cells[1][1] = COLOR_CYAN; tetromino->cells[2][1] = COLOR_CYAN; tetromino->cells[3][1] = COLOR_CYAN; tetromino->size = TETROMINO_SIZE; break; case TETROMINO_O: tetromino->cells[0][0] = COLOR_YELLOW; tetromino->cells[0][1] = COLOR_YELLOW; tetromino->cells[1][0] = COLOR_YELLOW; tetromino->cells[1][1] = COLOR_YELLOW; tetromino->size = TETROMINO_SIZE - 2; break; case TETROMINO_T: tetromino->cells[0][1] = COLOR_PURPLE; tetromino->cells[1][0] = COLOR_PURPLE; tetromino->cells[1][1] = COLOR_PURPLE; tetromino->cells[2][1] = COLOR_PURPLE; break; case TETROMINO_S: tetromino->cells[0][1] = COLOR_GREEN; tetromino->cells[1][0] = COLOR_GREEN; tetromino->cells[1][1] = COLOR_GREEN; tetromino->cells[2][0] = COLOR_GREEN; break; case TETROMINO_Z: tetromino->cells[0][0] = COLOR_RED; tetromino->cells[1][0] = COLOR_RED; tetromino->cells[1][1] = COLOR_RED; tetromino->cells[2][1] = COLOR_RED; break; case TETROMINO_J: tetromino->cells[0][0] = COLOR_BLUE; tetromino->cells[0][1] = COLOR_BLUE; tetromino->cells[1][1] = COLOR_BLUE; tetromino->cells[2][1] = COLOR_BLUE; break; case TETROMINO_L: tetromino->cells[0][1] = COLOR_ORANGE; tetromino->cells[1][1] = COLOR_ORANGE; tetromino->cells[2][0] = COLOR_ORANGE; tetromino->cells[2][1] = COLOR_ORANGE; break; } tetromino->type = indexTetromino; }
/* Initialize tetromino cells for every tipe of tetromino */ static void setTetramino(int indexTetramino, StcTetramino *tetramino) { /* Initialize tetromino cells to empty cells */ setMatrixCells(&tetramino->cells[0][0], 4, 4, EMPTY_CELL); /* Almost all the blocks have size 3 */ tetramino->size = 3; /* Initial configuration from: http://www.tetrisconcept.com/wiki/index.php/SRS */ switch (indexTetramino) { case TETROMINO_I: tetramino->cells[0][1] = TETRIS_COLOR_CYAN; tetramino->cells[1][1] = TETRIS_COLOR_CYAN; tetramino->cells[2][1] = TETRIS_COLOR_CYAN; tetramino->cells[3][1] = TETRIS_COLOR_CYAN; tetramino->size = 4; break; case TETROMINO_O: tetramino->cells[0][0] = TETRIS_COLOR_YELLOW; tetramino->cells[0][1] = TETRIS_COLOR_YELLOW; tetramino->cells[1][0] = TETRIS_COLOR_YELLOW; tetramino->cells[1][1] = TETRIS_COLOR_YELLOW; tetramino->size = 2; break; case TETROMINO_T: tetramino->cells[0][1] = TETRIS_COLOR_PURPLE; tetramino->cells[1][0] = TETRIS_COLOR_PURPLE; tetramino->cells[1][1] = TETRIS_COLOR_PURPLE; tetramino->cells[2][1] = TETRIS_COLOR_PURPLE; break; case TETROMINO_S: tetramino->cells[0][1] = TETRIS_COLOR_GREEN; tetramino->cells[1][0] = TETRIS_COLOR_GREEN; tetramino->cells[1][1] = TETRIS_COLOR_GREEN; tetramino->cells[2][0] = TETRIS_COLOR_GREEN; break; case TETROMINO_Z: tetramino->cells[0][0] = TETRIS_COLOR_RED; tetramino->cells[1][0] = TETRIS_COLOR_RED; tetramino->cells[1][1] = TETRIS_COLOR_RED; tetramino->cells[2][1] = TETRIS_COLOR_RED; break; case TETROMINO_J: tetramino->cells[0][0] = TETRIS_COLOR_BLUE; tetramino->cells[0][1] = TETRIS_COLOR_BLUE; tetramino->cells[1][1] = TETRIS_COLOR_BLUE; tetramino->cells[2][1] = TETRIS_COLOR_BLUE; break; case TETROMINO_L: tetramino->cells[0][1] = TETRIS_COLOR_ORANGE; tetramino->cells[1][1] = TETRIS_COLOR_ORANGE; tetramino->cells[2][0] = TETRIS_COLOR_ORANGE; tetramino->cells[2][1] = TETRIS_COLOR_ORANGE; break; } tetramino->type = indexTetramino; }
//旋转变换根据http://tetris.wikia.com/wiki/SRS void Game::rotateTetromino(bool clockwise) { //保存旋转后的数组形状,用来检测碰撞 int rotated[TETROMINO_SIZE][TETROMINO_SIZE]; //如果是O形,不用旋转 if(mFallingBlock.type == TETROMINO_O) { return; } setMatrixCells(&rotated[0][0], TETROMINO_SIZE, TETROMINO_SIZE, EMPTY_CELL); //将旋转后的cell存入rotated数组 for (int i = 0; i < mFallingBlock.size; ++i) { for (int j = 0; j < mFallingBlock.size; ++j) { if(clockwise) { rotated[mFallingBlock.size - j - 1][i] = mFallingBlock.cells[i][j]; } else { rotated[j][mFallingBlock.size - i - 1] = mFallingBlock.cells[i][j]; } } } /* 这里为了简单,我不允许kick wall,也就是当方块贴住墙壁的时候,不允许旋转 */ for (int i = 0; i < mFallingBlock.size; ++i) { for (int j = 0; j < mFallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { //检测和墙壁的碰撞 if (mFallingBlock.x + i < 0 //碰到左墙壁 //必须在墙内 || mFallingBlock.x + i >= BOARD_TILEMAP_WIDTH //碰到右墙壁 || mFallingBlock.y + j >= BOARD_TILEMAP_HEIGHT) {//碰到地面 return; } //检测和已有方块的碰撞 if(mMap[mFallingBlock.x + i][mFallingBlock.y + j] != EMPTY_CELL) { return; } } } } //如果没有碰撞,那么就用rotated替代mFallingBlock for (int i = 0; i < mFallingBlock.size; ++i) { for (int j = 0; j < mFallingBlock.size; ++j) { mFallingBlock.cells[i][j] = rotated[i][j]; } } onTetrominoMoved(); }
/* Start a new game */ static void startGame(StcGame *game) { int i; /* Initialize game data */ game->errorCode = ERROR_NONE; game->data->systemTime = platformGetSystemTime(); game->data->lastFallTime = game->data->systemTime; game->data->isOver = 0; game->isPaused = 0; game->showPreview = 1; game->data->events = EVENT_NONE; game->data->fallingDelay = STC_INIT_DELAY_FALL; #ifdef STC_SHOW_GHOST_PIECE game->showShadow = 1; #endif /* Initialize game statistics */ game->stats.score = 0; game->stats.lines = 0; game->stats.totalPieces = 0; game->stats.level = 0; for (i = 0; i < TETROMINO_TYPES; ++i) { game->stats.pieces[i] = 0; } /* Initialize game tile map */ setMatrixCells(&game->map[0][0], BOARD_TILEMAP_WIDTH, BOARD_TILEMAP_HEIGHT, EMPTY_CELL); /* Initialize falling tetromino */ setTetromino(platformRandom() % TETROMINO_TYPES, &game->fallingBlock); game->fallingBlock.x = (BOARD_TILEMAP_WIDTH - game->fallingBlock.size) / 2; game->fallingBlock.y = 0; /* Initialize preview tetromino */ setTetromino(platformRandom() % TETROMINO_TYPES, &game->nextBlock); /* Initialize events */ onTetrominoMoved(game); /* Initialize delayed autoshift */ game->data->delayLeft = -1; game->data->delayRight = -1; game->data->delayDown = -1; #ifdef STC_AUTO_ROTATION game->data->delayRotation = -1; #endif }
// Start a new game void Game::start() { // Initialize game data mErrorCode = ERROR_NONE; mSystemTime = mPlatform->getSystemTime(); mLastFallTime = mSystemTime; mIsOver = false; mIsPaused = false; mShowPreview = true; mEvents = EVENT_NONE; mFallingDelay = INIT_DELAY_FALL; #ifdef STC_SHOW_GHOST_PIECE mShowShadow = true; #endif // Initialize game statistics mStats.score = 0; mStats.lines = 0; mStats.totalPieces = 0; mStats.level = 0; for (int i = 0; i < TETROMINO_TYPES; ++i) { mStats.pieces[i] = 0; } // Initialize game tile map setMatrixCells(&mMap[0][0], BOARD_TILEMAP_WIDTH, BOARD_TILEMAP_HEIGHT, EMPTY_CELL); // Initialize falling tetromino setTetromino(mPlatform->random() % TETROMINO_TYPES, &mFallingBlock); mFallingBlock.x = (BOARD_TILEMAP_WIDTH - mFallingBlock.size) / 2; mFallingBlock.y = 0; // Initialize preview tetromino setTetromino(mPlatform->random() % TETROMINO_TYPES, &mNextBlock); // Initialize events onTetrominoMoved(); // Initialize delayed autoshift mDelayLeft = -1; mDelayRight = -1; mDelayDown = -1; #ifdef STC_AUTO_ROTATION mDelayRotation = -1; #endif }
/* * Rotate falling tetromino. If there are no collisions when the * tetromino is rotated this modifies the tetramino's cell buffer. */ void rotateTetramino(StcGame *game, int clockwise) { int i, j; int rotated[4][4]; /* temporary array to hold rotated cells */ /* If TETRAMINO_O is falling return immediately */ if (game->fallingBlock.type == TETROMINO_O) { return; /* rotation doesn't require any changes */ } /* Initialize rotated cells to blank */ setMatrixCells(&rotated[0][0], 4, 4, EMPTY_CELL); /* Copy rotated cells to the temporary array */ for (i = 0; i < game->fallingBlock.size; ++i) { for (j = 0; j < game->fallingBlock.size; ++j) { if (clockwise) { rotated[game->fallingBlock.size - j - 1][i] = game->fallingBlock.cells[i][j]; } else { rotated[j][game->fallingBlock.size - i - 1] = game->fallingBlock.cells[i][j]; } } } /* Check collision of the temporary array */ for (i = 0; i < game->fallingBlock.size; ++i) { for (j = 0; j < game->fallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { /* Check collision with left, right or bottom borders of the map */ if ((game->fallingBlock.x + i < 0) || (game->fallingBlock.x + i >= BOARD_WIDTH) || (game->fallingBlock.y + j >= BOARD_HEIGHT)) { return; /* there was collision therefore return */ } /* Check collision with existing cells in the map */ if (game->map[i + game->fallingBlock.x][j + game->fallingBlock.y] != EMPTY_CELL) { return; /* there was collision therefore return */ } } } } /* There are no collisions, replace tetramino cells with rotated cells */ for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { game->fallingBlock.cells[i][j] = rotated[i][j]; } } }
void Game::start() { mErrorCode = ERROR_NONE; mSystemTime = mPlatform->getSystemTime(); mLastFallTime = mSystemTime; mIsOver = false; mIsPaused = false; mShowPreview = true; mEvents = EVENT_NONE; mFallingDelay = INIT_DELAY_FALL; mShowShadow = false; mStats.score = 0; mStats.lines = 0; mStats.totalPieces = 0; mStats.level = 0; for (int i = 0; i < TETROMINO_TYPES; ++i) { mStats.pieces[i] = 0; } //初始化整个棋盘 setMatrixCells(&mMap[0][0], BOARD_TILEMAP_WIDTH, BOARD_TILEMAP_HEIGHT, EMPTY_CELL); //开始时随机设置一个nextblock作为第一个形状 setTetromino(mPlatform->random() % TETROMINO_TYPES, &mFallingBlock); //初始化为mFallingBlock的up-left mFallingBlock.x = BOARD_TILEMAP_WIDTH / 2 - mFallingBlock.size / 2; mFallingBlock.y = 0; setTetromino(mPlatform->random() % TETROMINO_TYPES, &mNextBlock); onTetrominoMoved(); mDelayDown = -1; mDelayLeft = -1; mDelayRight = -1; mDelayRotation = -1; }
/* * Rotate falling tetromino. If there are no collisions when the * tetromino is rotated this modifies the tetromino's cell buffer. */ void rotateTetromino(StcGame *game, int clockwise) { int i, j; #ifdef STC_WALL_KICK_ENABLED int wallDisplace; #endif int rotated[TETROMINO_SIZE][TETROMINO_SIZE]; /* temporary array to hold rotated cells */ /* If TETROMINO_O is falling return immediately */ if (game->fallingBlock.type == TETROMINO_O) { return; /* rotation doesn't require any changes */ } /* Initialize rotated cells to blank */ setMatrixCells(&rotated[0][0], TETROMINO_SIZE, TETROMINO_SIZE, EMPTY_CELL); /* Copy rotated cells to the temporary array */ for (i = 0; i < game->fallingBlock.size; ++i) { for (j = 0; j < game->fallingBlock.size; ++j) { if (clockwise) { rotated[game->fallingBlock.size - j - 1][i] = game->fallingBlock.cells[i][j]; } else { rotated[j][game->fallingBlock.size - i - 1] = game->fallingBlock.cells[i][j]; } } } #ifdef STC_WALL_KICK_ENABLED wallDisplace = 0; /* Check collision with left wall */ if (game->fallingBlock.x < 0) { for (i = 0; (wallDisplace == 0) && (i < -game->fallingBlock.x); ++i) { for (j = 0; j < game->fallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { wallDisplace = i - game->fallingBlock.x; break; } } } } /* Or check collision with right wall */ else if (game->fallingBlock.x > BOARD_TILEMAP_WIDTH - game->fallingBlock.size) { i = game->fallingBlock.size - 1; for (; (wallDisplace == 0) && (i >= BOARD_TILEMAP_WIDTH - game->fallingBlock.x); --i) { for (j = 0; j < game->fallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { wallDisplace = -game->fallingBlock.x - i + BOARD_TILEMAP_WIDTH - 1; break; } } } } /* Check collision with board floor and other cells on board */ for (i = 0; i < game->fallingBlock.size; ++i) { for (j = 0; j < game->fallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { /* Check collision with bottom border of the map */ if (game->fallingBlock.y + j >= BOARD_TILEMAP_HEIGHT) { return; /* there was collision therefore return */ } /* Check collision with existing cells in the map */ if (game->map[i + game->fallingBlock.x + wallDisplace][j + game->fallingBlock.y] != EMPTY_CELL) { return; /* there was collision therefore return */ } } } } /* Move the falling piece if there was wall collision and it's a legal move */ if (wallDisplace != 0) { game->fallingBlock.x += wallDisplace; } #else /* Check collision of the temporary array */ for (i = 0; i < game->fallingBlock.size; ++i) { for (j = 0; j < game->fallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { /* Check collision with left, right or bottom borders of the map */ if ((game->fallingBlock.x + i < 0) || (game->fallingBlock.x + i >= BOARD_TILEMAP_WIDTH) || (game->fallingBlock.y + j >= BOARD_TILEMAP_HEIGHT)) { return; /* there was collision therefore return */ } /* Check collision with existing cells in the map */ if (game->map[i + game->fallingBlock.x][j + game->fallingBlock.y] != EMPTY_CELL) { return; /* there was collision therefore return */ } } } } #endif /* There are no collisions, replace tetromino cells with rotated cells */ for (i = 0; i < TETROMINO_SIZE; ++i) { for (j = 0; j < TETROMINO_SIZE; ++j) { game->fallingBlock.cells[i][j] = rotated[i][j]; } } onTetrominoMoved(game); }
// Rotate falling tetromino. If there are no collisions when the // tetromino is rotated this modifies the tetromino's cell buffer. void Game::rotateTetromino(bool clockwise) { int i, j; int rotated[TETROMINO_SIZE][TETROMINO_SIZE]; // temporary array to hold rotated cells // If TETROMINO_O is falling return immediately if (mFallingBlock.type == TETROMINO_O) { return; // rotation doesn't require any changes } // Initialize rotated cells to blank setMatrixCells(&rotated[0][0], TETROMINO_SIZE, TETROMINO_SIZE, EMPTY_CELL); // Copy rotated cells to the temporary array for (i = 0; i < mFallingBlock.size; ++i) { for (j = 0; j < mFallingBlock.size; ++j) { if (clockwise) { rotated[mFallingBlock.size - j - 1][i] = mFallingBlock.cells[i][j]; } else { rotated[j][mFallingBlock.size - i - 1] = mFallingBlock.cells[i][j]; } } } #define STC_WALL_KICK_ENABLED #ifdef STC_WALL_KICK_ENABLED int wallDisplace = 0; // Check collision with left wall if (mFallingBlock.x < 0) { for (i = 0; (wallDisplace == 0) && (i < -mFallingBlock.x); ++i) { for (j = 0; j < mFallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { wallDisplace = i - mFallingBlock.x; break; } } } } // Or check collision with right wall else if (mFallingBlock.x > BOARD_TILEMAP_WIDTH - mFallingBlock.size) { i = mFallingBlock.size - 1; for (; (wallDisplace == 0) && (i >= BOARD_TILEMAP_WIDTH - mFallingBlock.x); --i) { for (j = 0; j < mFallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { wallDisplace = -mFallingBlock.x - i + BOARD_TILEMAP_WIDTH - 1; break; } } } } // Check collision with board floor and other cells on board for (i = 0; i < mFallingBlock.size; ++i) { for (j = 0; j < mFallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { // Check collision with bottom border of the map if (mFallingBlock.y + j >= BOARD_TILEMAP_HEIGHT) { return; // there was collision therefore return } // Check collision with existing cells in the map if (mMap[i + mFallingBlock.x + wallDisplace][j + mFallingBlock.y] != EMPTY_CELL) { return; // there was collision therefore return } } } } // Move the falling piece if there was wall collision and it's a legal move if (wallDisplace != 0) { mFallingBlock.x += wallDisplace; } #else // Check collision of the temporary array for (i = 0; i < mFallingBlock.size; ++i) { for (j = 0; j < mFallingBlock.size; ++j) { if (rotated[i][j] != EMPTY_CELL) { // Check collision with left, right or bottom borders of the map if ((mFallingBlock.x + i < 0) || (mFallingBlock.x + i >= BOARD_TILEMAP_WIDTH) || (mFallingBlock.y + j >= BOARD_TILEMAP_HEIGHT)) { return; // there was collision therefore return } // Check collision with existing cells in the map if (mMap[i + mFallingBlock.x][j + mFallingBlock.y] != EMPTY_CELL) { return; // there was collision therefore return } } } } #endif // STC_WALL_KICK_ENABLED // There are no collisions, replace tetromino cells with rotated cells for (i = 0; i < TETROMINO_SIZE; ++i) { for (j = 0; j < TETROMINO_SIZE; ++j) { mFallingBlock.cells[i][j] = rotated[i][j]; } } onTetrominoMoved(); }
void Game::setTetromino(int indexTetromino, Game::StcTetromino *tetromino) { setMatrixCells(&tetromino->cells[0][0], TETROMINO_SIZE, TETROMINO_SIZE, EMPTY_CELL); tetromino->size = TETROMINO_SIZE - 1; //why 3? // Initial configuration from http://tetris.wikia.com/wiki/SRS switch (indexTetromino) { case TETROMINO_I: tetromino->cells[0][1] = COLOR_CYAN; tetromino->cells[1][1] = COLOR_CYAN; tetromino->cells[2][1] = COLOR_CYAN; tetromino->cells[3][1] = COLOR_CYAN; tetromino->size = TETROMINO_SIZE; break; case TETROMINO_O: tetromino->cells[0][0] = COLOR_YELLOW; tetromino->cells[0][1] = COLOR_YELLOW; tetromino->cells[1][0] = COLOR_YELLOW; tetromino->cells[1][1] = COLOR_YELLOW; tetromino->size = TETROMINO_SIZE - 2; break; case TETROMINO_T: tetromino->cells[0][0] = COLOR_BLUE; tetromino->cells[1][0] = COLOR_BLUE; tetromino->cells[1][1] = COLOR_BLUE; tetromino->cells[2][1] = COLOR_BLUE; break; case TETROMINO_S: tetromino->cells[0][1] = COLOR_PURPLE; tetromino->cells[1][0] = COLOR_PURPLE; tetromino->cells[1][1] = COLOR_PURPLE; tetromino->cells[2][0] = COLOR_PURPLE; break; case TETROMINO_Z: tetromino->cells[0][0] = COLOR_GREEN; tetromino->cells[1][0] = COLOR_GREEN; tetromino->cells[1][1] = COLOR_GREEN; tetromino->cells[2][1] = COLOR_GREEN; break; case TETROMINO_J: tetromino->cells[0][0] = COLOR_RED; tetromino->cells[0][1] = COLOR_RED; tetromino->cells[1][1] = COLOR_RED; tetromino->cells[2][1] = COLOR_RED; break; case TETROMINO_L: tetromino->cells[0][1] = COLOR_ORANGE; tetromino->cells[1][1] = COLOR_ORANGE; tetromino->cells[2][1] = COLOR_ORANGE; tetromino->cells[2][0] = COLOR_ORANGE; break; default: break; } }