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
17KBlocksScene::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
51KBlocksScene::~KBlocksScene()
52{
53 delete [] maGameReadySignal;
54 delete [] maGameScoreList;
55 delete [] maGroupList;
56
57 delete mpGrafx;
58 delete mpSnd;
59}
60
61KBlocksItemGroup* KBlocksScene::getItemGroup(int index)
62{
63 return maGroupList[index];
64}
65
66KBlocksScore* KBlocksScene::getScoreHandler(int index)
67{
68 return maGameScoreList[index];
69}
70
71void 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
105void 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
124void KBlocksScene::setGamesPerLine(int count)
125{
126 mSceneGamesPerLine = count;
127}
128
129void 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
138void 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
147void 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
157void KBlocksScene::setSoundsEnabled(bool enabled)
158{
159 mpSnd->setSoundsEnabled(enabled);
160}
161
162void 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
173void 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
185void 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
206void 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
222void 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
258void 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
270void 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
289void KBlocksScene::greetPlayer()
290{
291 QString greets(i18n("Game Start!"));
292 showMessage( greets, 2000 );
293}
294
295void KBlocksScene::gameOverPlayer()
296{
297 QString greets(i18n("Game Over!"));
298 showMessage( greets, 2000 );
299}
300
301void KBlocksScene::gameOverMultiWin()
302{
303 QString gameOver(i18n("You Win!"));
304 showMessage( gameOver, 2000 );
305}
306
307void KBlocksScene::gameOverMultiLose()
308{
309 QString gameOver(i18n("You Lose!"));
310 showMessage( gameOver, 2000 );
311}
312
313void KBlocksScene::showMessage(const QString& message, int ms)
314{
315 mMessageBox->setMessageTimeout( ms );
316 mMessageBox->showMessage( message, KGamePopupItem::TopLeft );
317}
318
319void 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
379void 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
403void 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