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