File indexing completed on 2024-04-21 04:02:08

0001 /***************************************************************************
0002 *   KBlocks, a falling blocks game by KDE                                *
0003 *   SPDX-FileCopyrightText: 2010 Zhongjie Cai <squall.leonhart.cai@gmail.com>       *
0004 *                                                                         *
0005 *   SPDX-License-Identifier: GPL-2.0-or-later
0006 ***************************************************************************/
0007 #include "KBlocksSingleGame.h"
0008 #include "utils.h"
0009 #include <QRandomGenerator>
0010 #include <memory>
0011 
0012 KBlocksSingleGame::KBlocksSingleGame(int gameIndex, int fieldWidth, int fieldHeight, int showPieceCount, int messagePoolSize)
0013 {
0014     mGameIndex = gameIndex;
0015 
0016     mpField = new KBlocksField(fieldWidth, fieldHeight);
0017 
0018     mPieceCount = showPieceCount;
0019     mpPieceList = new KBlocksPiece*[mPieceCount];
0020     for (int i = 0; i < mPieceCount; i++) {
0021         mpPieceList[i] = new KBlocksPiece();
0022     }
0023 
0024     mpPieceGenerator = new KBlocksPieceGenerator();
0025     mpGameMessage = new KBlocksGameMessage(messagePoolSize);
0026     mpGameRecorder = nullptr;
0027 
0028     mCurrentGameState = GameState_Stop;
0029 
0030     mStandbyMode = false;
0031     mStandbyFlag = false;
0032 
0033     mGameInterval = 0;
0034     mGameStartTime = 0;
0035 }
0036 
0037 KBlocksSingleGame::~KBlocksSingleGame()
0038 {
0039     delete mpGameMessage;
0040     delete mpPieceGenerator;
0041 
0042     for (int i = 0; i < mPieceCount; i++) {
0043         delete mpPieceList[i];
0044     }
0045     delete [] mpPieceList;
0046 
0047     delete mpField;
0048 }
0049 
0050 KBlocksField *KBlocksSingleGame::getField()
0051 {
0052     return mpField;
0053 }
0054 
0055 int KBlocksSingleGame::getPieceCount()
0056 {
0057     return mPieceCount;
0058 }
0059 
0060 KBlocksPiece *KBlocksSingleGame::getPiece(int index)
0061 {
0062     if ((index < 0) || (index >= mPieceCount)) {
0063         return nullptr;
0064     }
0065     return mpPieceList[index];
0066 }
0067 
0068 bool KBlocksSingleGame::isActive()
0069 {
0070     if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
0071         return false;
0072     }
0073     return true;
0074 }
0075 
0076 bool KBlocksSingleGame::isGameRunning()
0077 {
0078     if (mCurrentGameState == GameState_Stop) {
0079         return false;
0080     }
0081     return true;
0082 }
0083 
0084 void KBlocksSingleGame::setGameStandbyMode(bool flag)
0085 {
0086     mStandbyMode = flag;
0087 }
0088 
0089 void KBlocksSingleGame::setGameInterval(int interval)
0090 {
0091     mGameInterval = interval;
0092 }
0093 
0094 void KBlocksSingleGame::setGameRecorder(KBlocksGameRecorder *p)
0095 {
0096     mpGameRecorder = p;
0097 }
0098 
0099 int KBlocksSingleGame::forceUpdateGame()
0100 {
0101     return doUpdateGame(true);
0102 }
0103 
0104 int KBlocksSingleGame::updateGame()
0105 {
0106     return doUpdateGame(false);
0107 }
0108 
0109 int KBlocksSingleGame::punishGame(int lineCount, int punishSeed)
0110 {
0111     if (mCurrentGameState == GameState_Stop) {
0112         return GameResult_None;
0113     }
0114 
0115     int width = mpField->getWidth();
0116 
0117     int gameResult = GameResult_None;
0118 
0119     if (mpGameRecorder) {
0120         mpGameRecorder->append(mGameIndex, RecordDataType_PunishLineCount, lineCount);
0121         mpGameRecorder->append(mGameIndex, RecordDataType_PunishLineSeed, punishSeed);
0122     }
0123 
0124     QRandomGenerator randomGenerator(punishSeed);
0125     int punishIndex = 0;
0126     for (int i = 0; i < lineCount; i++) {
0127         setCurrentPiece(0, -1, 0);
0128         punishIndex = randomGenerator.bounded(width);
0129         mpField->addPunishLine(lineCount, punishIndex);
0130     }
0131 
0132     if (lineCount > 0) {
0133         mpGameMessage->putGameAction(GameAction_Punish_Line, lineCount);
0134     }
0135 
0136     return gameResult;
0137 }
0138 
0139 bool KBlocksSingleGame::setCurrentPiece(int xPos, int yPos, int rotation)
0140 {
0141     if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
0142         return false;
0143     }
0144 
0145     std::unique_ptr<KBlocksPiece> tmpPiece(new KBlocksPiece());
0146 
0147     tmpPiece->fromValue(mpPieceList[0]->toValue());
0148     tmpPiece->setPosX(mpPieceList[0]->getPosX() + xPos);
0149     if (mpPieceList[0]->getPosY() + yPos < 0) {
0150         tmpPiece->setPosY(0);
0151     } else {
0152         tmpPiece->setPosY(mpPieceList[0]->getPosY() + yPos);
0153     }
0154     tmpPiece->setRotation(mpPieceList[0]->getRotation() + rotation);
0155 
0156     if (checkPieceTouchGround(tmpPiece.get())) {
0157         return false;
0158     }
0159 
0160     mpPieceList[0]->setPosX(tmpPiece->getPosX());
0161     mpPieceList[0]->setPosY(tmpPiece->getPosY());
0162     mpPieceList[0]->setRotation(tmpPiece->getRotation());
0163 
0164     if (mpGameRecorder) {
0165         if (xPos < 0) {
0166             mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceLeft, 1);
0167         }
0168         if (xPos > 0) {
0169             mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceRight, 1);
0170         }
0171         if (yPos < 0) {
0172             mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceUp, 1);
0173         }
0174         if (yPos > 0) {
0175             mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceDown, 1);
0176         }
0177         if (rotation < 0) {
0178             mpGameRecorder->append(mGameIndex, RecordDataType_RotatePieceCW, 1);
0179         }
0180         if (rotation > 0) {
0181             mpGameRecorder->append(mGameIndex, RecordDataType_RotatePieceCCW, 1);
0182         }
0183     }
0184 
0185     return true;
0186 }
0187 
0188 int KBlocksSingleGame::startGame(int seed)
0189 {
0190     if (mCurrentGameState != GameState_Stop) {
0191         return mCurrentGameState;
0192     }
0193 
0194     mpPieceGenerator->genList(seed);
0195 
0196     for (int i = 0; i < mPieceCount; i++) {
0197         mpPieceList[i]->fromValue(mpPieceGenerator->getPiece());
0198     }
0199 
0200     mpPieceList[0]->setPosX(mpField->getWidth() / 2);
0201     mpPieceList[0]->setPosY(0);
0202 
0203     mpPieceList[1]->setPosX(2);
0204     mpPieceList[1]->setPosY(2);
0205 
0206     mpGameMessage->clearGameResult();
0207     mpGameMessage->clearGameAction();
0208 
0209     mCurrentGameState = GameState_Running;
0210 
0211     mGameStartTime = Utils::getMillisecOfNow();
0212 
0213     return mCurrentGameState;
0214 }
0215 
0216 int KBlocksSingleGame::stopGame()
0217 {
0218     if (mCurrentGameState != GameState_Stop) {
0219         mCurrentGameState = GameState_Stop;
0220         Q_EMIT gameStopped();
0221     }
0222 
0223     return mCurrentGameState;
0224 }
0225 
0226 int KBlocksSingleGame::pauseGame(bool flag)
0227 {
0228     if ((mCurrentGameState == GameState_Running) && flag) {
0229         mCurrentGameState = GameState_Pause;
0230     } else if ((mCurrentGameState == GameState_Pause) && (!flag)) {
0231         mCurrentGameState = GameState_Running;
0232 
0233         mGameStartTime = Utils::getMillisecOfNow();
0234     }
0235 
0236     return mCurrentGameState;
0237 }
0238 
0239 int KBlocksSingleGame::continueGame()
0240 {
0241     if ((mCurrentGameState != GameState_Stop) && mStandbyFlag) {
0242         mStandbyFlag = false;
0243 
0244         mGameStartTime = Utils::getMillisecOfNow();
0245     }
0246 
0247     return mCurrentGameState;
0248 }
0249 
0250 bool KBlocksSingleGame::pickGameResult(int *result)
0251 {
0252     return mpGameMessage->pickGameResult(result);
0253 }
0254 
0255 bool KBlocksSingleGame::pickGameAction(int *type, int *action)
0256 {
0257     return mpGameMessage->pickGameAction(type, action);
0258 }
0259 
0260 int KBlocksSingleGame::doUpdateGame(bool force)
0261 {
0262     if (mCurrentGameState == GameState_Stop) {
0263         return GameResult_Game_Over;
0264     } else if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
0265         return GameResult_None;
0266     }
0267 
0268     timeLong tmpCurTime = Utils::getMillisecOfNow();
0269 
0270     int gameResult = GameResult_None;
0271 
0272     if (force) {
0273         runGameOneStep(&gameResult);
0274     } else {
0275         if (mGameInterval < 0) {
0276             return gameResult;
0277         }
0278 
0279         while (1) {
0280             if (mGameStartTime + mGameInterval > tmpCurTime) {
0281                 break;
0282             }
0283 
0284             mGameStartTime += mGameInterval;
0285 
0286             if (runGameOneStep(&gameResult) || (mGameInterval == 0)) {
0287                 break;
0288             }
0289         }
0290     }
0291 
0292     return gameResult;
0293 }
0294 
0295 bool KBlocksSingleGame::runGameOneStep(int *gameResult)
0296 {
0297     if (!setCurrentPiece(0, 1, 0)) {
0298         *gameResult = GameResult_Next_Piece;
0299 
0300         freezePieceToField(mpPieceList[0]);
0301 
0302         *gameResult += removeFieldLines();
0303 
0304         if (mStandbyMode) {
0305             mStandbyFlag = true;
0306         }
0307 
0308         prepareNextPiece();
0309         if (checkPieceTouchGround(mpPieceList[0])) {
0310             *gameResult = GameResult_Game_Over;
0311             mpGameMessage->putGameResult(-1);
0312             stopGame();
0313         }
0314 
0315         if (mpGameRecorder) {
0316             mpGameRecorder->append(mGameIndex, RecordDataType_GameOneStep, 1);
0317         }
0318 
0319         return true;
0320     } else {
0321         *gameResult = GameResult_One_Step;
0322 
0323         return false;
0324     }
0325 }
0326 
0327 bool KBlocksSingleGame::checkPieceTouchGround(KBlocksPiece *p)
0328 {
0329     for (int i = 0; i < 4; i++) {
0330         int posX = p->getCellPosX(i);
0331         int posY = p->getCellPosY(i);
0332         if (mpField->getCell(posX, posY)) {
0333             return true;
0334         }
0335     }
0336     return false;
0337 }
0338 
0339 void KBlocksSingleGame::freezePieceToField(KBlocksPiece *p)
0340 {
0341     mpGameMessage->putGameAction(GameAction_Freeze_Piece_Color, mpPieceList[0]->getType());
0342     for (int i = 0; i < 4; i++) {
0343         int posX = p->getCellPosX(i);
0344         int posY = p->getCellPosY(i);
0345         mpField->setCell(posX, posY, true);
0346         mpGameMessage->putGameAction(GameAction_Freeze_Piece_X, posX);
0347         mpGameMessage->putGameAction(GameAction_Freeze_Piece_Y, posY);
0348     }
0349 }
0350 
0351 int KBlocksSingleGame::removeFieldLines()
0352 {
0353     int lineCount = 0;
0354 
0355     int maxLines = mpField->getHeight();
0356     for (int i = 0; i < maxLines; i++) {
0357         if (mpField->checkFilledLine(i)) {
0358             mpGameMessage->putGameAction(GameAction_Remove_Line, i);
0359             mpField->removeFilledLine(i);
0360             lineCount++;
0361         }
0362     }
0363 
0364     if (lineCount > 0) {
0365         mpGameMessage->putGameResult(lineCount);
0366     }
0367 
0368     return lineCount;
0369 }
0370 
0371 void KBlocksSingleGame::prepareNextPiece()
0372 {
0373     for (int i = 0; i < mPieceCount - 1; i++) {
0374         mpPieceList[i]->fromValue(mpPieceList[i + 1]->toValue());
0375     }
0376 
0377     int pieceValue = mpPieceGenerator->getPiece();
0378     mpPieceList[mPieceCount - 1]->fromValue(pieceValue);
0379 
0380     mpPieceList[0]->setPosX(mpField->getWidth() / 2);
0381     mpPieceList[0]->setPosY(0);
0382 
0383     mpPieceList[1]->setPosX(2);
0384     mpPieceList[1]->setPosY(2);
0385 
0386     for (int i = 0; i < 4; i++) {
0387         int posX = mpPieceList[0]->getCellPosX(i);
0388         int posY = mpPieceList[0]->getCellPosY(i);
0389         mpGameMessage->putGameAction(GameAction_New_Piece_X, posX);
0390         mpGameMessage->putGameAction(GameAction_New_Piece_Y, posY);
0391     }
0392 }
0393 
0394 #include "moc_KBlocksSingleGame.cpp"