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 "KBlocksScene.h" |
12 | |
13 | #include "settings.h" |
14 | |
15 | #include <QVarLengthArray> |
16 | |
17 | KBlocksScene::KBlocksScene(GameLogicInterface * p, int capacity) |
18 | { |
19 | mpGameLogic = p; |
20 | mGameStarted = false; |
21 | |
22 | mSnapshotMode = false; |
23 | mTopGameLevel = 0; |
24 | mGroupCount = 0; |
25 | mMaxCapacity = capacity; |
26 | mSceneGamesPerLine = 4; |
27 | mGameAnimEnabled = true; |
28 | mWaitForAllUpdate = false; |
29 | |
30 | maGroupList = new KBlocksItemGroup*[capacity](); |
31 | maGameScoreList = new KBlocksScore*[capacity](); |
32 | maGameReadySignal = new bool[capacity](); |
33 | |
34 | QString themeFile(Settings::theme()); |
35 | mpGrafx = new KBlocksGraphics(themeFile); |
36 | mpSnd = new KBlocksSound(themeFile); |
37 | |
38 | int width = (capacity >= mSceneGamesPerLine) ? mSceneGamesPerLine : (capacity % mSceneGamesPerLine); |
39 | int height = (int)(capacity / (mSceneGamesPerLine + 1)) + 1; |
40 | setSceneRect(0, 0, mpGrafx->m_View_Size_Width * width, |
41 | mpGrafx->m_View_Size_Height * height); |
42 | |
43 | setItemIndexMethod(NoIndex); |
44 | |
45 | mUpdateInterval = 50; |
46 | mUpdateTimer.setInterval(mUpdateInterval); |
47 | connect(&mUpdateTimer, SIGNAL(timeout()), SLOT(updateGame())); |
48 | mUpdateTimer.stop(); |
49 | } |
50 | |
51 | KBlocksScene::~KBlocksScene() |
52 | { |
53 | delete [] maGameReadySignal; |
54 | delete [] maGameScoreList; |
55 | delete [] maGroupList; |
56 | |
57 | delete mpGrafx; |
58 | delete mpSnd; |
59 | } |
60 | |
61 | KBlocksItemGroup* KBlocksScene::getItemGroup(int index) |
62 | { |
63 | return maGroupList[index]; |
64 | } |
65 | |
66 | KBlocksScore* KBlocksScene::getScoreHandler(int index) |
67 | { |
68 | return maGameScoreList[index]; |
69 | } |
70 | |
71 | void KBlocksScene::createGameItemGroups(int groupCount, bool snapshotMode) |
72 | { |
73 | if (groupCount > mMaxCapacity) |
74 | { |
75 | mGroupCount = mMaxCapacity; |
76 | } |
77 | mGroupCount = groupCount; |
78 | mSnapshotMode = snapshotMode; |
79 | mTopGameLevel = 0; |
80 | |
81 | for(int i = 0; i < mGroupCount; i++) |
82 | { |
83 | maGroupList[i] = new KBlocksItemGroup(i, mpGameLogic->getSingleGame(i), mpGrafx, mpSnd, snapshotMode); |
84 | maGroupList[i]->setUpdateInterval(mUpdateInterval); |
85 | maGroupList[i]->setGameAnimEnabled(mGameAnimEnabled); |
86 | maGroupList[i]->setWaitForAllUpdate(mWaitForAllUpdate); |
87 | addItem(maGroupList[i]); |
88 | |
89 | maGameScoreList[i] = new KBlocksScore(); |
90 | maGameScoreList[i]->setLevelUpFactor(KBlocksScore_Level_x_Level_x_Factor, 1000); |
91 | maGameScoreList[i]->setScoreUpFactor(10); |
92 | |
93 | maGameReadySignal[i] = false; |
94 | connect(maGroupList[i], SIGNAL(readyForAction(int)), this, SLOT(readyForAction(int))); |
95 | } |
96 | |
97 | updateDimensions(); |
98 | |
99 | //Our Message Item, hidden by default |
100 | mMessageBox = new KGamePopupItem(); |
101 | mMessageBox->setMessageOpacity(0.9); |
102 | addItem(mMessageBox); |
103 | } |
104 | |
105 | void KBlocksScene::deleteGameItemGroups() |
106 | { |
107 | removeItem(mMessageBox); |
108 | delete mMessageBox; |
109 | |
110 | for(int i = 0; i < mGroupCount; i++) |
111 | { |
112 | disconnect(maGroupList[i], SIGNAL(readyForAction(int)), this, SLOT(readyForAction(int))); |
113 | |
114 | delete maGameScoreList[i]; |
115 | maGameScoreList[i] = 0; |
116 | |
117 | removeItem(maGroupList[i]); |
118 | delete maGroupList[i]; |
119 | maGroupList[i] = 0; |
120 | } |
121 | mGroupCount = 0; |
122 | } |
123 | |
124 | void KBlocksScene::setGamesPerLine(int count) |
125 | { |
126 | mSceneGamesPerLine = count; |
127 | } |
128 | |
129 | void KBlocksScene::setGameAnimEnabled(bool flag) |
130 | { |
131 | mGameAnimEnabled = flag; |
132 | for(int i = 0; i < mGroupCount; i++) |
133 | { |
134 | maGroupList[i]->setGameAnimEnabled(flag); |
135 | } |
136 | } |
137 | |
138 | void KBlocksScene::setWaitForAllUpdate(bool flag) |
139 | { |
140 | mWaitForAllUpdate = flag; |
141 | for(int i = 0; i < mGroupCount; i++) |
142 | { |
143 | maGroupList[i]->setWaitForAllUpdate(flag); |
144 | } |
145 | } |
146 | |
147 | void KBlocksScene::setUpdateInterval(int interval) |
148 | { |
149 | mUpdateInterval = interval; |
150 | mUpdateTimer.setInterval(mUpdateInterval); |
151 | for(int i = 0; i < mGroupCount; i++) |
152 | { |
153 | maGroupList[i]->setUpdateInterval(mUpdateInterval); |
154 | } |
155 | } |
156 | |
157 | void KBlocksScene::setSoundsEnabled(bool enabled) |
158 | { |
159 | mpSnd->setSoundsEnabled(enabled); |
160 | } |
161 | |
162 | void KBlocksScene::readSettings(const QSize & viewSize) |
163 | { |
164 | if (mpGrafx->theme()->fileName()!=Settings::theme()) |
165 | { |
166 | mpGrafx->loadTheme(Settings::theme()); |
167 | mpSnd->loadTheme(Settings::theme()); |
168 | mpGrafx->adjustForSize(viewSize); |
169 | updateDimensions(); |
170 | } |
171 | } |
172 | |
173 | void KBlocksScene::viewScaled(const QSize& /*newsize*/) |
174 | { |
175 | /* |
176 | //Temporarily halt game timer while resizing elements |
177 | if (gameState==Game_Active) stepTimer.stop(); |
178 | grafx->adjustForSize(newsize); |
179 | updateDimensions(); |
180 | //Do not restart if game was paused |
181 | if (gameState==Game_Active) stepTimer.start(); |
182 | */ |
183 | } |
184 | |
185 | void KBlocksScene::startGame() |
186 | { |
187 | if (mGameStarted) |
188 | { |
189 | return; |
190 | } |
191 | mGameStarted = true; |
192 | |
193 | mTopGameLevel = 0; |
194 | for(int i = 0; i < mGroupCount; i++) |
195 | { |
196 | maGroupList[i]->startGame(); |
197 | } |
198 | |
199 | if (!mSnapshotMode) |
200 | { |
201 | mUpdateTimer.start(); |
202 | QTimer::singleShot(500, this, SLOT(greetPlayer())); |
203 | } |
204 | } |
205 | |
206 | void KBlocksScene::stopGame() |
207 | { |
208 | if (!mGameStarted) |
209 | { |
210 | return; |
211 | } |
212 | mGameStarted = false; |
213 | |
214 | for(int i = 0; i < mGroupCount; i++) |
215 | { |
216 | maGroupList[i]->stopGame(); |
217 | } |
218 | |
219 | mUpdateTimer.stop(); |
220 | } |
221 | |
222 | void KBlocksScene::pauseGame(bool flag, bool fromUI) |
223 | { |
224 | if (!mGameStarted) |
225 | { |
226 | return; |
227 | } |
228 | |
229 | QString resuming(i18n("Game Resumed!" )); |
230 | QString pausing(i18n("Game Paused!" )); |
231 | |
232 | for(int i = 0; i < mGroupCount; i++) |
233 | { |
234 | maGroupList[i]->pauseGame(flag); |
235 | } |
236 | |
237 | if (!mSnapshotMode) |
238 | { |
239 | if (flag) |
240 | { |
241 | mUpdateTimer.stop(); |
242 | if (!fromUI) |
243 | { |
244 | showMessage(pausing, 2000); |
245 | } |
246 | } |
247 | else |
248 | { |
249 | mUpdateTimer.start(); |
250 | if (!fromUI) |
251 | { |
252 | showMessage(resuming, 2000); |
253 | } |
254 | } |
255 | } |
256 | } |
257 | |
258 | void KBlocksScene::addScore(int gameIndex, int lineCount) |
259 | { |
260 | if (!mSnapshotMode) |
261 | { |
262 | return; |
263 | } |
264 | maGameScoreList[gameIndex]->addScore(lineCount); |
265 | emit scoreChanged(gameIndex, maGameScoreList[gameIndex]->getScorePoint(), |
266 | maGameScoreList[gameIndex]->getLineCount(), |
267 | maGameScoreList[gameIndex]->getGameLevel()); |
268 | } |
269 | |
270 | void KBlocksScene::updateDimensions() |
271 | { |
272 | // TODO : Reset item position and scale |
273 | int width = (mGroupCount >= mSceneGamesPerLine) ? mSceneGamesPerLine : (mGroupCount % mSceneGamesPerLine); |
274 | int height = (int)(mGroupCount / (mSceneGamesPerLine + 1)) + 1; |
275 | |
276 | setSceneRect(0, 0, mpGrafx->m_View_Size_Width * width, |
277 | mpGrafx->m_View_Size_Height * height); |
278 | |
279 | for(int i = 0; i < mGroupCount; i++) |
280 | { |
281 | int left = mpGrafx->m_View_Size_Width * (i % mSceneGamesPerLine); |
282 | int top = mpGrafx->m_View_Size_Height * ((int)(i / mSceneGamesPerLine)); |
283 | |
284 | maGroupList[i]->setPos(left, top); |
285 | maGroupList[i]->refreshPosition(); |
286 | } |
287 | } |
288 | |
289 | void KBlocksScene::greetPlayer() |
290 | { |
291 | QString greets(i18n("Game Start!" )); |
292 | showMessage( greets, 2000 ); |
293 | } |
294 | |
295 | void KBlocksScene::gameOverPlayer() |
296 | { |
297 | QString greets(i18n("Game Over!" )); |
298 | showMessage( greets, 2000 ); |
299 | } |
300 | |
301 | void KBlocksScene::gameOverMultiWin() |
302 | { |
303 | QString gameOver(i18n("You Win!" )); |
304 | showMessage( gameOver, 2000 ); |
305 | } |
306 | |
307 | void KBlocksScene::gameOverMultiLose() |
308 | { |
309 | QString gameOver(i18n("You Lose!" )); |
310 | showMessage( gameOver, 2000 ); |
311 | } |
312 | |
313 | void KBlocksScene::showMessage(const QString& message, int ms) |
314 | { |
315 | mMessageBox->setMessageTimeout( ms ); |
316 | mMessageBox->showMessage( message, KGamePopupItem::TopLeft ); |
317 | } |
318 | |
319 | void KBlocksScene::updateGame() |
320 | { |
321 | if (mSnapshotMode) |
322 | { |
323 | return; |
324 | } |
325 | |
326 | QVarLengthArray<int, 16> removedLines(mGroupCount); |
327 | int gameCount = mpGameLogic->updateGame(removedLines.data()); |
328 | |
329 | for(int i = 0; i < mGroupCount; i++) |
330 | { |
331 | if (removedLines[i] > 0) |
332 | { |
333 | if (maGameScoreList[i]->addScore(removedLines[i])) |
334 | { |
335 | int tmpLevel = maGameScoreList[i]->getGameLevel(); |
336 | if (mTopGameLevel < tmpLevel) |
337 | { |
338 | mpGameLogic->levelUpGame(tmpLevel - mTopGameLevel); |
339 | mTopGameLevel = tmpLevel; |
340 | } |
341 | } |
342 | emit scoreChanged(i, maGameScoreList[i]->getScorePoint(), |
343 | maGameScoreList[i]->getLineCount(), |
344 | maGameScoreList[i]->getGameLevel()); |
345 | } |
346 | else if (removedLines[i] == -1) |
347 | { |
348 | maGroupList[i]->stopGame(); |
349 | if (mGroupCount == 1) |
350 | { |
351 | QTimer::singleShot(500, this, SLOT(gameOverPlayer())); |
352 | emit isHighscore(0, maGameScoreList[0]->getScorePoint(), |
353 | maGameScoreList[0]->getGameLevel()); |
354 | } |
355 | else |
356 | { |
357 | if (i == 0) |
358 | { |
359 | for (int j = 0; j < mGroupCount; j++) |
360 | { |
361 | maGroupList[j]->stopGame(); |
362 | } |
363 | QTimer::singleShot(500, this, SLOT(gameOverMultiLose())); |
364 | emit isHighscore(0, maGameScoreList[0]->getScorePoint(), |
365 | maGameScoreList[0]->getGameLevel()); |
366 | } |
367 | else if (gameCount <= 1) |
368 | { |
369 | maGroupList[0]->stopGame(); |
370 | QTimer::singleShot(500, this, SLOT(gameOverMultiWin())); |
371 | emit isHighscore(0, maGameScoreList[0]->getScorePoint(), |
372 | maGameScoreList[0]->getGameLevel()); |
373 | } |
374 | } |
375 | } |
376 | } |
377 | } |
378 | |
379 | void KBlocksScene::readyForAction(int groupID) |
380 | { |
381 | maGameReadySignal[groupID] = true; |
382 | bool allReady = true; |
383 | for(int i = 0; i < mGroupCount; i++) |
384 | { |
385 | if (!maGameReadySignal[i]) |
386 | { |
387 | allReady = false; |
388 | } |
389 | } |
390 | if (allReady) |
391 | { |
392 | for(int i = 0; i < mGroupCount; i++) |
393 | { |
394 | if (mpGameLogic->getSingleGame(i)->isGameRunning()) |
395 | { |
396 | maGameReadySignal[i] = false; |
397 | } |
398 | } |
399 | mpGameLogic->continueGame(); |
400 | } |
401 | } |
402 | |
403 | void KBlocksScene::drawBackground( QPainter * painter, const QRectF & rect ) |
404 | { |
405 | if (mpGrafx->renderer()->isValid()) |
406 | { |
407 | mpGrafx->renderer()->render(painter, QString("BACKGROUND" ), rect); |
408 | } |
409 | } |
410 | |
411 | |
412 | |