1 /***************************************************************************
2 * KBlocks, a falling blocks game by KDE *
3 * Copyright (C) 2010 Zhongjie Cai <squall.leonhart.cai@gmail.com> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10 #include "KBlocksSingleGame.h"
11 #include "utils.h"
12 #include <QRandomGenerator>
13
KBlocksSingleGame(int gameIndex,int fieldWidth,int fieldHeight,int showPieceCount,int messagePoolSize)14 KBlocksSingleGame::KBlocksSingleGame(int gameIndex, int fieldWidth, int fieldHeight, int showPieceCount, int messagePoolSize)
15 {
16 mGameIndex = gameIndex;
17
18 mpField = new KBlocksField(fieldWidth, fieldHeight);
19
20 mPieceCount = showPieceCount;
21 mpPieceList = new KBlocksPiece*[mPieceCount];
22 for (int i = 0; i < mPieceCount; i++) {
23 mpPieceList[i] = new KBlocksPiece();
24 }
25
26 mpPieceGenerator = new KBlocksPieceGenerator();
27 mpGameMessage = new KBlocksGameMessage(messagePoolSize);
28 mpGameRecorder = nullptr;
29
30 mCurrentGameState = GameState_Stop;
31
32 mStandbyMode = false;
33 mStandbyFlag = false;
34
35 mGameInterval = 0;
36 mGameStartTime = 0;
37 }
38
~KBlocksSingleGame()39 KBlocksSingleGame::~KBlocksSingleGame()
40 {
41 delete mpGameMessage;
42 delete mpPieceGenerator;
43
44 for (int i = 0; i < mPieceCount; i++) {
45 delete mpPieceList[i];
46 }
47 delete [] mpPieceList;
48
49 delete mpField;
50 }
51
getField()52 KBlocksField *KBlocksSingleGame::getField()
53 {
54 return mpField;
55 }
56
getPieceCount()57 int KBlocksSingleGame::getPieceCount()
58 {
59 return mPieceCount;
60 }
61
getPiece(int index)62 KBlocksPiece *KBlocksSingleGame::getPiece(int index)
63 {
64 if ((index < 0) || (index >= mPieceCount)) {
65 return nullptr;
66 }
67 return mpPieceList[index];
68 }
69
isActive()70 bool KBlocksSingleGame::isActive()
71 {
72 if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
73 return false;
74 }
75 return true;
76 }
77
isGameRunning()78 bool KBlocksSingleGame::isGameRunning()
79 {
80 if (mCurrentGameState == GameState_Stop) {
81 return false;
82 }
83 return true;
84 }
85
setGameStandbyMode(bool flag)86 void KBlocksSingleGame::setGameStandbyMode(bool flag)
87 {
88 mStandbyMode = flag;
89 }
90
setGameInterval(int interval)91 void KBlocksSingleGame::setGameInterval(int interval)
92 {
93 mGameInterval = interval;
94 }
95
setGameRecorder(KBlocksGameRecorder * p)96 void KBlocksSingleGame::setGameRecorder(KBlocksGameRecorder *p)
97 {
98 mpGameRecorder = p;
99 }
100
forceUpdateGame()101 int KBlocksSingleGame::forceUpdateGame()
102 {
103 return doUpdateGame(true);
104 }
105
updateGame()106 int KBlocksSingleGame::updateGame()
107 {
108 return doUpdateGame(false);
109 }
110
punishGame(int lineCount,int punishSeed)111 int KBlocksSingleGame::punishGame(int lineCount, int punishSeed)
112 {
113 if (mCurrentGameState == GameState_Stop) {
114 return GameResult_None;
115 }
116
117 int width = mpField->getWidth();
118
119 int gameResult = GameResult_None;
120
121 if (mpGameRecorder) {
122 mpGameRecorder->append(mGameIndex, RecordDataType_PunishLineCount, lineCount);
123 mpGameRecorder->append(mGameIndex, RecordDataType_PunishLineSeed, punishSeed);
124 }
125
126 int punishIndex = 0;
127 auto random = QRandomGenerator::global();
128 for (int i = 0; i < lineCount; i++) {
129 setCurrentPiece(0, -1, 0);
130 punishIndex = random->bounded(width);
131 mpField->addPunishLine(lineCount, punishIndex);
132 }
133
134 if (lineCount > 0) {
135 mpGameMessage->putGameAction(GameAction_Punish_Line, lineCount);
136 }
137
138 return gameResult;
139 }
140
setCurrentPiece(int xPos,int yPos,int rotation)141 bool KBlocksSingleGame::setCurrentPiece(int xPos, int yPos, int rotation)
142 {
143 if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
144 return false;
145 }
146 //FIXME: new without delete (is the new really necessary here?)
147 QScopedPointer<KBlocksPiece> tmpPiece(new KBlocksPiece());
148
149 tmpPiece->fromValue(mpPieceList[0]->toValue());
150 tmpPiece->setPosX(mpPieceList[0]->getPosX() + xPos);
151 if (mpPieceList[0]->getPosY() + yPos < 0) {
152 tmpPiece->setPosY(0);
153 } else {
154 tmpPiece->setPosY(mpPieceList[0]->getPosY() + yPos);
155 }
156 tmpPiece->setRotation(mpPieceList[0]->getRotation() + rotation);
157
158 if (checkPieceTouchGround(tmpPiece.data())) {
159 return false;
160 }
161
162 mpPieceList[0]->setPosX(tmpPiece->getPosX());
163 mpPieceList[0]->setPosY(tmpPiece->getPosY());
164 mpPieceList[0]->setRotation(tmpPiece->getRotation());
165
166 if (mpGameRecorder) {
167 if (xPos < 0) {
168 mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceLeft, 1);
169 }
170 if (xPos > 0) {
171 mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceRight, 1);
172 }
173 if (yPos < 0) {
174 mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceUp, 1);
175 }
176 if (yPos > 0) {
177 mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceDown, 1);
178 }
179 if (rotation < 0) {
180 mpGameRecorder->append(mGameIndex, RecordDataType_RotatePieceCW, 1);
181 }
182 if (rotation > 0) {
183 mpGameRecorder->append(mGameIndex, RecordDataType_RotatePieceCCW, 1);
184 }
185 }
186
187 return true;
188 }
189
startGame(int seed)190 int KBlocksSingleGame::startGame(int seed)
191 {
192 if (mCurrentGameState != GameState_Stop) {
193 return mCurrentGameState;
194 }
195
196 mpPieceGenerator->genList(seed);
197
198 for (int i = 0; i < mPieceCount; i++) {
199 mpPieceList[i]->fromValue(mpPieceGenerator->getPiece());
200 }
201
202 mpPieceList[0]->setPosX(mpField->getWidth() / 2);
203 mpPieceList[0]->setPosY(0);
204
205 mpPieceList[1]->setPosX(2);
206 mpPieceList[1]->setPosY(2);
207
208 mpGameMessage->clearGameResult();
209 mpGameMessage->clearGameAction();
210
211 mCurrentGameState = GameState_Running;
212
213 mGameStartTime = Utils::getMillisecOfNow();
214
215 return mCurrentGameState;
216 }
217
stopGame()218 int KBlocksSingleGame::stopGame()
219 {
220 if (mCurrentGameState != GameState_Stop) {
221 mCurrentGameState = GameState_Stop;
222 Q_EMIT gameStopped();
223 }
224
225 return mCurrentGameState;
226 }
227
pauseGame(bool flag)228 int KBlocksSingleGame::pauseGame(bool flag)
229 {
230 if ((mCurrentGameState == GameState_Running) && flag) {
231 mCurrentGameState = GameState_Pause;
232 } else if ((mCurrentGameState == GameState_Pause) && (!flag)) {
233 mCurrentGameState = GameState_Running;
234
235 mGameStartTime = Utils::getMillisecOfNow();
236 }
237
238 return mCurrentGameState;
239 }
240
continueGame()241 int KBlocksSingleGame::continueGame()
242 {
243 if ((mCurrentGameState != GameState_Stop) && mStandbyFlag) {
244 mStandbyFlag = false;
245
246 mGameStartTime = Utils::getMillisecOfNow();
247 }
248
249 return mCurrentGameState;
250 }
251
pickGameResult(int * result)252 bool KBlocksSingleGame::pickGameResult(int *result)
253 {
254 return mpGameMessage->pickGameResult(result);
255 }
256
pickGameAction(int * type,int * action)257 bool KBlocksSingleGame::pickGameAction(int *type, int *action)
258 {
259 return mpGameMessage->pickGameAction(type, action);
260 }
261
doUpdateGame(bool force)262 int KBlocksSingleGame::doUpdateGame(bool force)
263 {
264 if (mCurrentGameState == GameState_Stop) {
265 return GameResult_Game_Over;
266 } else if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
267 return GameResult_None;
268 }
269
270 timeLong tmpCurTime = Utils::getMillisecOfNow();
271
272 int gameResult = GameResult_None;
273
274 if (force) {
275 runGameOneStep(&gameResult);
276 } else {
277 if (mGameInterval < 0) {
278 return gameResult;
279 }
280
281 while (1) {
282 if (mGameStartTime + mGameInterval > tmpCurTime) {
283 break;
284 }
285
286 mGameStartTime += mGameInterval;
287
288 if (runGameOneStep(&gameResult) || (mGameInterval == 0)) {
289 break;
290 }
291 }
292 }
293
294 return gameResult;
295 }
296
runGameOneStep(int * gameResult)297 bool KBlocksSingleGame::runGameOneStep(int *gameResult)
298 {
299 if (!setCurrentPiece(0, 1, 0)) {
300 *gameResult = GameResult_Next_Piece;
301
302 freezePieceToField(mpPieceList[0]);
303
304 *gameResult += removeFieldLines();
305
306 if (mStandbyMode) {
307 mStandbyFlag = true;
308 }
309
310 prepareNextPiece();
311 if (checkPieceTouchGround(mpPieceList[0])) {
312 *gameResult = GameResult_Game_Over;
313 mpGameMessage->putGameResult(-1);
314 stopGame();
315 }
316
317 if (mpGameRecorder) {
318 mpGameRecorder->append(mGameIndex, RecordDataType_GameOneStep, 1);
319 }
320
321 return true;
322 } else {
323 *gameResult = GameResult_One_Step;
324
325 return false;
326 }
327 }
328
checkPieceTouchGround(KBlocksPiece * p)329 bool KBlocksSingleGame::checkPieceTouchGround(KBlocksPiece *p)
330 {
331 for (int i = 0; i < 4; i++) {
332 int posX = p->getCellPosX(i);
333 int posY = p->getCellPosY(i);
334 if (mpField->getCell(posX, posY)) {
335 return true;
336 }
337 }
338 return false;
339 }
340
freezePieceToField(KBlocksPiece * p)341 void KBlocksSingleGame::freezePieceToField(KBlocksPiece *p)
342 {
343 mpGameMessage->putGameAction(GameAction_Freeze_Piece_Color, mpPieceList[0]->getType());
344 for (int i = 0; i < 4; i++) {
345 int posX = p->getCellPosX(i);
346 int posY = p->getCellPosY(i);
347 mpField->setCell(posX, posY, true);
348 mpGameMessage->putGameAction(GameAction_Freeze_Piece_X, posX);
349 mpGameMessage->putGameAction(GameAction_Freeze_Piece_Y, posY);
350 }
351 }
352
removeFieldLines()353 int KBlocksSingleGame::removeFieldLines()
354 {
355 int lineCount = 0;
356
357 int maxLines = mpField->getHeight();
358 for (int i = 0; i < maxLines; i++) {
359 if (mpField->checkFilledLine(i)) {
360 mpGameMessage->putGameAction(GameAction_Remove_Line, i);
361 mpField->removeFilledLine(i);
362 lineCount++;
363 }
364 }
365
366 if (lineCount > 0) {
367 mpGameMessage->putGameResult(lineCount);
368 }
369
370 return lineCount;
371 }
372
prepareNextPiece()373 void KBlocksSingleGame::prepareNextPiece()
374 {
375 for (int i = 0; i < mPieceCount - 1; i++) {
376 mpPieceList[i]->fromValue(mpPieceList[i + 1]->toValue());
377 }
378
379 int pieceValue = mpPieceGenerator->getPiece();
380 mpPieceList[mPieceCount - 1]->fromValue(pieceValue);
381
382 mpPieceList[0]->setPosX(mpField->getWidth() / 2);
383 mpPieceList[0]->setPosY(0);
384
385 mpPieceList[1]->setPosX(2);
386 mpPieceList[1]->setPosY(2);
387
388 for (int i = 0; i < 4; i++) {
389 int posX = mpPieceList[0]->getCellPosX(i);
390 int posY = mpPieceList[0]->getCellPosY(i);
391 mpGameMessage->putGameAction(GameAction_New_Piece_X, posX);
392 mpGameMessage->putGameAction(GameAction_New_Piece_Y, posY);
393 }
394 }
395
396