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 | |
16 | KBlocksSingleGame::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 | |
42 | KBlocksSingleGame::~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 | |
56 | KBlocksField* KBlocksSingleGame::getField() |
57 | { |
58 | return mpField; |
59 | } |
60 | |
61 | int KBlocksSingleGame::getPieceCount() |
62 | { |
63 | return mPieceCount; |
64 | } |
65 | |
66 | KBlocksPiece* KBlocksSingleGame::getPiece(int index) |
67 | { |
68 | if ((index < 0) || (index >= mPieceCount)) |
69 | { |
70 | return 0; |
71 | } |
72 | return mpPieceList[index]; |
73 | } |
74 | |
75 | bool KBlocksSingleGame::isActive() |
76 | { |
77 | if ((mCurrentGameState != GameState_Running) || mStandbyFlag) |
78 | { |
79 | return false; |
80 | } |
81 | return true; |
82 | } |
83 | |
84 | bool KBlocksSingleGame::isGameRunning() |
85 | { |
86 | if (mCurrentGameState == GameState_Stop) |
87 | { |
88 | return false; |
89 | } |
90 | return true; |
91 | } |
92 | |
93 | void KBlocksSingleGame::setGameStandbyMode(bool flag) |
94 | { |
95 | mStandbyMode = flag; |
96 | } |
97 | |
98 | void KBlocksSingleGame::setGameInterval(int interval) |
99 | { |
100 | mGameInterval = interval; |
101 | } |
102 | |
103 | void KBlocksSingleGame::setGameRecorder(KBlocksGameRecorder * p) |
104 | { |
105 | mpGameRecorder = p; |
106 | } |
107 | |
108 | int KBlocksSingleGame::forceUpdateGame() |
109 | { |
110 | return doUpdateGame(true); |
111 | } |
112 | |
113 | int KBlocksSingleGame::updateGame() |
114 | { |
115 | return doUpdateGame(false); |
116 | } |
117 | |
118 | int 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 | |
152 | bool 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 | |
213 | int 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 | |
243 | int KBlocksSingleGame::stopGame() |
244 | { |
245 | mCurrentGameState = GameState_Stop; |
246 | |
247 | return mCurrentGameState; |
248 | } |
249 | |
250 | int 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 | |
266 | int KBlocksSingleGame::continueGame() |
267 | { |
268 | if ((mCurrentGameState != GameState_Stop) && mStandbyFlag) |
269 | { |
270 | mStandbyFlag = false; |
271 | |
272 | mGameStartTime = getMillisecOfNow(); |
273 | } |
274 | |
275 | return mCurrentGameState; |
276 | } |
277 | |
278 | bool KBlocksSingleGame::pickGameResult(int * result) |
279 | { |
280 | return mpGameMessage->pickGameResult(result); |
281 | } |
282 | |
283 | bool KBlocksSingleGame::pickGameAction(int * type, int * action) |
284 | { |
285 | return mpGameMessage->pickGameAction(type, action); |
286 | } |
287 | |
288 | int 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 | |
333 | bool 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 | |
371 | bool 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 | |
385 | void 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 | |
398 | int 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 | |
421 | void 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 | |
446 | timeLong 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 | |