1 | /*************************************************************************** |
2 | * KBlocks, a falling blocks game for KDE * |
3 | * Copyright (C) 2010 Mauricio Piacentini <mauricio@tabuleiro.com> * |
4 | * Zhongjie Cai <squall.leonhart.cai@gmail.com> * |
5 | * * |
6 | * This program is free software; you can redistribute it and/or modify * |
7 | * it under the terms of the GNU General Public License as published by * |
8 | * the Free Software Foundation; either version 2 of the License, or * |
9 | * (at your option) any later version. * |
10 | ***************************************************************************/ |
11 | #include "KBlocksItemGroup.h" |
12 | |
13 | KBlocksItemGroup::KBlocksItemGroup(int groupID, SingleGameInterface * p, KBlocksGraphics * pG, KBlocksSound * pS, bool snapshotMode) |
14 | { |
15 | mGroupID = groupID; |
16 | mpSingleGame = p; |
17 | mpGrafx = pG; |
18 | mpSnd = pS; |
19 | |
20 | updateGraphicInfo(); |
21 | |
22 | mpGameLayout = new KBlocksLayout(mpSingleGame->getField(), mpSingleGame->getPiece(0), mpSingleGame->getPiece(1)); |
23 | |
24 | mpBackground = new KBlocksSvgItem(mpGameLayout, -1, 0, 0); |
25 | mpBackground->setSharedRenderer(mpGrafx->renderer()); |
26 | mpBackground->setElementId("VIEW" ); |
27 | addToGroup(mpBackground); |
28 | |
29 | mMaxPrepareCellNum = PREPARE_AREA_WIDTH * PREPARE_AREA_WIDTH; |
30 | maPrepareCells = new KBlocksSvgItem*[mMaxPrepareCellNum]; |
31 | for(int i = 0; i < mMaxPrepareCellNum; i++) |
32 | { |
33 | maPrepareCells[i] = new KBlocksSvgItem( mpGameLayout, KBlocksSvgItem_PrepareArea, |
34 | i % PREPARE_AREA_WIDTH, i / PREPARE_AREA_WIDTH); |
35 | maPrepareCells[i]->setSharedRenderer(mpGrafx->renderer()); |
36 | maPrepareCells[i]->setElementId("BLOCK_0" ); |
37 | maPrepareCells[i]->setVisible(false); |
38 | addToGroup(maPrepareCells[i]); |
39 | } |
40 | |
41 | mMaxFreezeCellNum = (mFieldWidth * mFieldHeight); |
42 | maFreezeCells = new KBlocksSvgItem*[mMaxFreezeCellNum]; |
43 | for(int i = 0; i < mMaxFreezeCellNum; i++) |
44 | { |
45 | maFreezeCells[i] = new KBlocksSvgItem( mpGameLayout, KBlocksSvgItem_FieldArea, |
46 | i % mFieldWidth, i / mFieldWidth); |
47 | maFreezeCells[i]->setSharedRenderer(mpGrafx->renderer()); |
48 | maFreezeCells[i]->setElementId("BLOCK_0" ); |
49 | maFreezeCells[i]->setVisible(false); |
50 | addToGroup(maFreezeCells[i]); |
51 | } |
52 | |
53 | mGameAnimEnabled = true; |
54 | mWaitForAllUpdate = false; |
55 | mpAnimator = new KBlocksAnimator(); |
56 | connect(mpAnimator, SIGNAL(animFinished(int)), this, SLOT(endAnimation(int))); |
57 | |
58 | mFadeInItems.clear(); |
59 | mFadeOutItems.clear(); |
60 | mDropItems.clear(); |
61 | |
62 | mUpdateInterval = 50; |
63 | mUpdateTimer.setInterval(mUpdateInterval); |
64 | if (snapshotMode) |
65 | { |
66 | connect(&mUpdateTimer, SIGNAL(timeout()), SLOT(updateSnapshot())); |
67 | } |
68 | else |
69 | { |
70 | connect(&mUpdateTimer, SIGNAL(timeout()), SLOT(updateGame())); |
71 | } |
72 | mUpdateTimer.stop(); |
73 | } |
74 | |
75 | KBlocksItemGroup::~KBlocksItemGroup() |
76 | { |
77 | delete mpAnimator; |
78 | |
79 | for(int i = 0; i < mMaxFreezeCellNum; i++) |
80 | { |
81 | removeFromGroup(maFreezeCells[i]); |
82 | delete maFreezeCells[i]; |
83 | } |
84 | delete [] maFreezeCells; |
85 | |
86 | for(int i = 0; i < mMaxPrepareCellNum; i++) |
87 | { |
88 | removeFromGroup(maPrepareCells[i]); |
89 | delete maPrepareCells[i]; |
90 | } |
91 | delete [] maPrepareCells; |
92 | |
93 | removeFromGroup(mpBackground); |
94 | delete mpBackground; |
95 | |
96 | delete mpGameLayout; |
97 | } |
98 | |
99 | void KBlocksItemGroup::setUpdateInterval(int interval) |
100 | { |
101 | mUpdateInterval = interval; |
102 | mUpdateTimer.setInterval(mUpdateInterval); |
103 | } |
104 | |
105 | void KBlocksItemGroup::setGameAnimEnabled(bool flag) |
106 | { |
107 | mGameAnimEnabled = flag; |
108 | } |
109 | |
110 | void KBlocksItemGroup::setWaitForAllUpdate(bool flag) |
111 | { |
112 | mWaitForAllUpdate = flag; |
113 | } |
114 | |
115 | void KBlocksItemGroup::refreshPosition() |
116 | { |
117 | updateGraphicInfo(); |
118 | |
119 | mpBackground->setElementId("VIEW" ); |
120 | mpBackground->setPos(0, 0); |
121 | |
122 | for(int i = 0; i < mMaxPrepareCellNum; i++) |
123 | { |
124 | maPrepareCells[i]->setPos(mPrepareLeft + mItemSize * (i % PREPARE_AREA_WIDTH), |
125 | mPrepareTop + mItemSize * (i / PREPARE_AREA_WIDTH)); |
126 | } |
127 | |
128 | for(int i = 0; i < mMaxFreezeCellNum; i++) |
129 | { |
130 | maFreezeCells[i]->setPos(mFieldLeft + mItemSize * (i % mFieldWidth), |
131 | mFieldTop + mItemSize * (i / mFieldWidth)); |
132 | } |
133 | } |
134 | |
135 | void KBlocksItemGroup::startGame() |
136 | { |
137 | mUpdateTimer.start(); |
138 | } |
139 | |
140 | void KBlocksItemGroup::stopGame() |
141 | { |
142 | mUpdateTimer.stop(); |
143 | } |
144 | |
145 | void KBlocksItemGroup::pauseGame(bool flag) |
146 | { |
147 | if (flag) |
148 | { |
149 | mUpdateTimer.stop(); |
150 | } |
151 | else |
152 | { |
153 | mUpdateTimer.start(); |
154 | } |
155 | } |
156 | |
157 | void KBlocksItemGroup::updateGame() |
158 | { |
159 | int gameResult = mpSingleGame->updateGame(); |
160 | |
161 | if (gameResult == GameResult_Game_Over) |
162 | { |
163 | mUpdateTimer.stop(); |
164 | return; |
165 | } |
166 | |
167 | bool hasRemovedLines = updateLayout(); |
168 | |
169 | if (hasRemovedLines && mGameAnimEnabled) |
170 | { |
171 | mUpdateTimer.stop(); |
172 | fadeOutOldLine(); |
173 | dropFreezeLine(); |
174 | } |
175 | else |
176 | { |
177 | refreshItems(); |
178 | if (hasRemovedLines) |
179 | { |
180 | if (!mWaitForAllUpdate) |
181 | { |
182 | mpSingleGame->continueGame(); |
183 | } |
184 | else |
185 | { |
186 | emit readyForAction(mGroupID); |
187 | } |
188 | } |
189 | } |
190 | } |
191 | |
192 | void KBlocksItemGroup::updateSnapshot() |
193 | { |
194 | mpGameLayout->updateSnapshot(); |
195 | refreshItems(); |
196 | } |
197 | |
198 | void KBlocksItemGroup::endAnimation(int animType) |
199 | { |
200 | switch(animType) |
201 | { |
202 | case KBlocks_Animation_Fade_In: |
203 | mpAnimator->deleteFadeAnim(); |
204 | if (!mWaitForAllUpdate) |
205 | { |
206 | mpSingleGame->continueGame(); |
207 | } |
208 | else |
209 | { |
210 | emit readyForAction(mGroupID); |
211 | } |
212 | mUpdateTimer.start(); |
213 | break; |
214 | case KBlocks_Animation_Fade_Out: |
215 | mpAnimator->deleteFadeAnim(); |
216 | fadeInNewPiece(); |
217 | break; |
218 | case KBlocks_Animation_Drop: |
219 | mpAnimator->deleteDropAnim(); |
220 | break; |
221 | default: |
222 | break; |
223 | } |
224 | } |
225 | |
226 | bool KBlocksItemGroup::updateLayout() |
227 | { |
228 | int tmpActionType = GameAction_None; |
229 | int tmpActionData = 0; |
230 | QList<int> tmpDataList; |
231 | |
232 | bool hasAnim = false; |
233 | int pieceCellCount = mpSingleGame->getPiece(0)->getCellCount() * 2; |
234 | |
235 | mpGameLayout->beginUpdate(&tmpDataList); |
236 | refreshItemByPos(tmpDataList); |
237 | tmpDataList.clear(); |
238 | |
239 | mRemovedLine.clear(); |
240 | mPunishLine.clear(); |
241 | mNewPiecePos.clear(); |
242 | |
243 | while(mpSingleGame->pickGameAction(&tmpActionType, &tmpActionData)) |
244 | { |
245 | switch(tmpActionType) |
246 | { |
247 | case GameAction_Freeze_Piece_Color: |
248 | tmpDataList.append(tmpActionData); |
249 | for(int i = 0; i < pieceCellCount; i++) |
250 | { |
251 | tmpActionType = GameAction_None; |
252 | mpSingleGame->pickGameAction(&tmpActionType, &tmpActionData); |
253 | tmpDataList.append(tmpActionData); |
254 | } |
255 | mpGameLayout->updateLayout(KBlocksLayout_Update_FreezePiece, tmpDataList); |
256 | tmpDataList.takeFirst(); |
257 | refreshItemByPos(tmpDataList); |
258 | break; |
259 | case GameAction_Remove_Line: |
260 | mRemovedLine.append(tmpActionData); |
261 | tmpDataList.append(tmpActionData); |
262 | mpGameLayout->updateLayout(KBlocksLayout_Update_RemoveLine, tmpDataList); |
263 | hasAnim = true; |
264 | break; |
265 | case GameAction_Punish_Line: |
266 | mPunishLine.append(tmpActionData); |
267 | tmpDataList.append(tmpActionData); |
268 | mpGameLayout->updateLayout(KBlocksLayout_Update_PunishLine, tmpDataList); |
269 | break; |
270 | case GameAction_New_Piece_X: |
271 | case GameAction_New_Piece_Y: |
272 | mNewPiecePos.append(tmpActionData); |
273 | hasAnim = true; |
274 | break; |
275 | } |
276 | tmpActionType = GameAction_None; |
277 | tmpActionData = 0; |
278 | tmpDataList.clear(); |
279 | } |
280 | |
281 | mpGameLayout->endUpdate(); |
282 | |
283 | return hasAnim; |
284 | } |
285 | |
286 | void KBlocksItemGroup::refreshItems() |
287 | { |
288 | for(int i = 0; i < mMaxFreezeCellNum; i++) |
289 | { |
290 | maFreezeCells[i]->updateSelf(); |
291 | } |
292 | for(int i = 0; i < mMaxPrepareCellNum; i++) |
293 | { |
294 | maPrepareCells[i]->updateSelf(); |
295 | } |
296 | } |
297 | |
298 | void KBlocksItemGroup::refreshItemByPos(const QList<int> & dataList) |
299 | { |
300 | int posX = 0; |
301 | int posY = 0; |
302 | int pieceCellCount = dataList.size() / 2; |
303 | for(int i = 0; i < pieceCellCount; i++) |
304 | { |
305 | posX = dataList[i * 2]; |
306 | posY = dataList[i * 2 + 1]; |
307 | if ((posX >= 0) && (posX < mFieldWidth) |
308 | && (posY >= 0) && (posY < mFieldHeight)) |
309 | { |
310 | maFreezeCells[posX + posY * mFieldWidth]->updateSelf(); |
311 | } |
312 | } |
313 | } |
314 | |
315 | void KBlocksItemGroup::fadeInNewPiece() |
316 | { |
317 | int count = mNewPiecePos.size() / 2; |
318 | |
319 | int posX[4] = {-1, -1, -1, -1}; |
320 | int posY[4] = {-1, -1, -1, -1}; |
321 | |
322 | for(int i = 0; i < count; i++) |
323 | { |
324 | posX[i] = mNewPiecePos[i * 2]; |
325 | posY[i] = mNewPiecePos[i * 2 + 1]; |
326 | } |
327 | |
328 | mFadeInItems.clear(); |
329 | for(int i = 0; i < 4; i++) |
330 | { |
331 | if ((posX[i] >= 0 && posX[i] < mFieldWidth) |
332 | && (posY[i] >= 0 && posY[i] < mFieldHeight)) |
333 | { |
334 | maFreezeCells[posX[i] + posY[i] * mFieldWidth]->setOpacity(0); |
335 | mFadeInItems.append(maFreezeCells[posX[i] + posY[i] * mFieldWidth]); |
336 | } |
337 | } |
338 | |
339 | for(int i = 0; i < mMaxFreezeCellNum; i++) |
340 | { |
341 | maFreezeCells[i]->updateSelf(); |
342 | } |
343 | foreach(KBlocksSvgItem * tmpItem, mFadeOutItems) |
344 | { |
345 | tmpItem->setOpacity(1); |
346 | tmpItem->stopOpAnim(); |
347 | } |
348 | foreach(KBlocksSvgItem * tmpItem, mDropItems) |
349 | { |
350 | tmpItem->stopPosAnim(); |
351 | } |
352 | |
353 | for(int i = 0; i < PREPARE_AREA_WIDTH * PREPARE_AREA_WIDTH; i++) |
354 | { |
355 | maPrepareCells[i]->updateSelf(); |
356 | if (maPrepareCells[i]->isVisible()) |
357 | { |
358 | maPrepareCells[i]->setOpacity(0); |
359 | mFadeInItems.append(maPrepareCells[i]); |
360 | } |
361 | } |
362 | |
363 | mpAnimator->createFadeAnim(mFadeInItems, FADE_ANIM_TIME_LINE, QTimeLine::Forward); |
364 | } |
365 | |
366 | void KBlocksItemGroup::fadeOutOldLine() |
367 | { |
368 | int count = mRemovedLine.size(); |
369 | |
370 | mFadeOutItems.clear(); |
371 | for(int i = 0; i < count; i++) |
372 | { |
373 | for(int j = 0; j < mFieldWidth; j++) |
374 | { |
375 | maFreezeCells[j + mRemovedLine[i] * mFieldWidth]->startOpAnim(); |
376 | mFadeOutItems.append(maFreezeCells[j + mRemovedLine[i] * mFieldWidth]); |
377 | } |
378 | } |
379 | |
380 | for(int i = 0; i < PREPARE_AREA_WIDTH * PREPARE_AREA_WIDTH; i++) |
381 | { |
382 | if (maPrepareCells[i]->isVisible()) |
383 | { |
384 | mFadeOutItems.append(maPrepareCells[i]); |
385 | } |
386 | } |
387 | |
388 | mpAnimator->createFadeAnim(mFadeOutItems, FADE_ANIM_TIME_LINE, QTimeLine::Backward); |
389 | } |
390 | |
391 | void KBlocksItemGroup::dropFreezeLine() |
392 | { |
393 | int count = mRemovedLine.size(); |
394 | |
395 | int *fallLine = new int[mFieldHeight]; |
396 | int removeLine = 0; |
397 | |
398 | if (count == 0) |
399 | { |
400 | delete [] fallLine; |
401 | return; |
402 | } |
403 | |
404 | for(int i = mFieldHeight - 1; i >= 0; i--) |
405 | { |
406 | if (removeLine < count) |
407 | { |
408 | if (i == mRemovedLine[removeLine]) |
409 | { |
410 | fallLine[i] = 0; |
411 | removeLine++; |
412 | } |
413 | else |
414 | { |
415 | fallLine[i] = removeLine; |
416 | } |
417 | } |
418 | else |
419 | { |
420 | fallLine[i] = removeLine; |
421 | } |
422 | } |
423 | |
424 | mDropItems.clear(); |
425 | for(int i = 0; i < mFieldHeight; i++) |
426 | { |
427 | if (fallLine[i] > 0) |
428 | { |
429 | QPointF target; |
430 | target.setX(0); |
431 | target.setY(fallLine[i] * mItemSize); |
432 | for(int j = 0; j < mFieldWidth; j++) |
433 | { |
434 | if (maFreezeCells[j + i * mFieldWidth]->isVisible()) |
435 | { |
436 | maFreezeCells[j + i * mFieldWidth]->startPosAnim(target); |
437 | mDropItems.append(maFreezeCells[j + i * mFieldWidth]); |
438 | } |
439 | } |
440 | } |
441 | } |
442 | |
443 | mpAnimator->createDropAnim(mDropItems, DROP_ANIM_TIME_LINE, QTimeLine::Forward); |
444 | delete[] fallLine; |
445 | } |
446 | |
447 | void KBlocksItemGroup::updateGraphicInfo() |
448 | { |
449 | mItemSize = mpGrafx->m_Block_Size; |
450 | mPrepareLeft = mpGrafx->m_PreviewArea_CenterPoint_X - PREPARE_AREA_WIDTH * mpGrafx->m_Block_Size / 2; |
451 | mPrepareTop = mpGrafx->m_PreviewArea_CenterPoint_Y - PREPARE_AREA_WIDTH * mpGrafx->m_Block_Size / 2; |
452 | mFieldLeft = mpGrafx->m_PlayArea_OffsetPoint_X; |
453 | mFieldTop = mpGrafx->m_PlayArea_OffsetPoint_Y; |
454 | mFieldWidth = mpGrafx->m_PlayArea_NumberOfBlocks_X; |
455 | mFieldHeight = mpGrafx->m_PlayArea_NumberOfBlocks_Y; |
456 | } |
457 | |