2 * Copyright 2007-2008 Thomas Gallinari <tg8187@yahoo.fr>
3 * Copyright 2007-2008 Alexandre Galinier <alex.galinier@hotmail.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "gamescene.h"
27 GameScene::GameScene(Game
* p_game
) : m_game(p_game
), m_kapmanItem(0), m_mazeItem(0) {
28 connect(p_game
, SIGNAL(levelStarted(bool)), SLOT(intro(bool)));
29 connect(p_game
, SIGNAL(gameStarted()), this, SLOT(start()));
30 connect(p_game
, SIGNAL(pauseChanged(bool, bool)), this, SLOT(setPaused(bool, bool)));
31 connect(p_game
, SIGNAL(elementEaten(qreal
, qreal
)), this, SLOT(hideElement(qreal
, qreal
)));
32 connect(p_game
, SIGNAL(bonusOn()), this, SLOT(displayBonus()));
33 connect(p_game
, SIGNAL(bonusOff()), this, SLOT(hideBonus()));
34 connect(p_game
, SIGNAL(dataChanged(Game::InformationTypes
)), this, SLOT(updateInfo(Game::InformationTypes
)));
36 // Connection between Game and GameScene for the display of won points when a bonus or a ghost is eaten
37 connect(p_game
, SIGNAL(pointsToDisplay(long, qreal
, qreal
)), this, SLOT(displayPoints(long, qreal
, qreal
)));
39 // Create the theme instance
40 m_theme
= new KGameTheme();
42 // Set the pixmap cache limit to improve performance
43 setItemIndexMethod(NoIndex
);
44 m_cache
= new KPixmapCache("kapman_cache");
45 m_cache
->setCacheLimit(3 * 1024);
48 m_renderer
= new KSvgRenderer();
51 // Create the MazeItem
52 m_mazeItem
= new MazeItem();
53 // Give the maze the shared renderer to avoid loading the whole SVG file again
54 m_mazeItem
->setSharedRenderer(m_renderer
);
55 // Set the element Id to the right value
56 m_mazeItem
->setElementId("maze");
57 m_mazeItem
->setZValue(-2);
59 // Create the KapmanItem
60 m_kapmanItem
= new KapmanItem(p_game
->getKapman());
61 m_kapmanItem
->setSharedRenderer(m_renderer
);
62 m_kapmanItem
->setElementId("kapman_0");
63 // Corrects the position of the KapmanItem
64 m_kapmanItem
->update(p_game
->getKapman()->getX(), p_game
->getKapman()->getY());
65 m_kapmanItem
->setZValue(2);
66 // Stops the Kapman animation
67 m_kapmanItem
->stopAnim();
69 // Create the GhostItems
70 for (int i
= 0; i
< p_game
->getGhosts().size(); ++i
) {
71 GhostItem
* ghost
= new GhostItem(p_game
->getGhosts()[i
]);
72 ghost
->setSharedRenderer(m_renderer
);
73 ghost
->setElementId(p_game
->getGhosts()[i
]->getImageId());
74 ghost
->update(p_game
->getGhosts()[i
]->getX(), p_game
->getGhosts()[i
]->getY());
75 // At the beginning, the ghosts are above the kapman because they eat him
77 m_ghostItems
.append(ghost
);
79 // Create the Pill and Energizer items
80 m_elementItems
= new ElementItem
**[m_game
->getMaze()->getNbRows()];
81 for (int i
= 0; i
< m_game
->getMaze()->getNbRows(); ++i
) {
82 m_elementItems
[i
] = new ElementItem
*[m_game
->getMaze()->getNbColumns()];
83 for (int j
= 0; j
< m_game
->getMaze()->getNbColumns(); ++j
) {
84 if (m_game
->getMaze()->getCell(i
, j
).getElement() != NULL
) {
85 // Create the element and set the image
86 ElementItem
* element
= new ElementItem(m_game
->getMaze()->getCell(i
, j
).getElement());
87 element
->setSharedRenderer(m_renderer
);
88 element
->setElementId(m_game
->getMaze()->getCell(i
,j
).getElement()->getImageId());
89 element
->update(m_game
->getMaze()->getCell(i
, j
).getElement()->getX(), m_game
->getMaze()->getCell(i
, j
).getElement()->getY());
90 m_elementItems
[i
][j
] = element
;
92 m_elementItems
[i
][j
] = NULL
;
96 // Create the Bonus item
97 m_bonusItem
= new ElementItem(m_game
->getBonus());
98 m_bonusItem
->setSharedRenderer(m_renderer
);
99 m_bonusItem
->setElementId("bonus1");
101 // All elements are created, update theme properties
103 updateThemeProperties();
105 // Create the introduction labels
106 m_introLabel
= new QGraphicsTextItem(i18n("GET READY!!!"));
107 m_introLabel
->setFont(QFont("Helvetica", 25, QFont::Bold
, false));
108 m_introLabel
->setDefaultTextColor(QColor("#FFFF00"));
109 m_introLabel
->setZValue(4);
110 m_introLabel2
= new QGraphicsTextItem(i18n("Press any arrow key to start"));
111 m_introLabel2
->setFont(QFont("Helvetica", 15, QFont::Bold
, false));
112 m_introLabel2
->setDefaultTextColor(QColor("#FFFF00"));
113 m_introLabel2
->setZValue(4);
114 // Create the new level label
115 m_newLevelLabel
= new QGraphicsTextItem();
116 m_newLevelLabel
->setFont(QFont("Helvetica", 35, QFont::Bold
, false));
117 m_newLevelLabel
->setDefaultTextColor(QColor("#FFFF00"));
118 m_newLevelLabel
->setZValue(4);
119 // Create the pause label
120 m_pauseLabel
= new QGraphicsTextItem(i18n("PAUSED"));
121 m_pauseLabel
->setFont(QFont("Helvetica", 35, QFont::Bold
, false));
122 m_pauseLabel
->setDefaultTextColor(QColor("#FFFF00"));
123 m_pauseLabel
->setZValue(4);
124 // Create the score label
125 m_scoreLabel
= new QGraphicsTextItem();
126 m_scoreLabel
->setFont(QFont("Helvetica", 15, QFont::Bold
, false));
127 m_scoreLabel
->setDefaultTextColor(QColor("#FFFFFF"));
128 // Create the lives label
129 m_livesLabel
= new QGraphicsTextItem();
130 m_livesLabel
->setFont(QFont("Helvetica", 15, QFont::Bold
, false));
131 m_livesLabel
->setDefaultTextColor(QColor("#FFFFFF"));
132 // Create the level label
133 m_levelLabel
= new QGraphicsTextItem();
134 m_levelLabel
->setFont(QFont("Helvetica", 15, QFont::Bold
, false));
135 m_levelLabel
->setDefaultTextColor(QColor("#FFFFFF"));
137 // Display the MazeItem
139 // Display the KapmanItem
140 addItem(m_kapmanItem
);
141 // Display each GhostItem
142 for (int i
= 0; i
< m_ghostItems
.size(); ++i
) {
143 addItem(m_ghostItems
[i
]);
145 // Initialize the information labels (score, lives and label)
146 updateInfo(Game::AllInfo
);
147 // Display the score label
148 addItem(m_scoreLabel
);
149 m_scoreLabel
->setPos(Cell::SIZE
, height() + Cell::SIZE
);
150 // Display the level label
151 addItem(m_levelLabel
);
152 m_levelLabel
->setPos((width() - m_levelLabel
->boundingRect().width()) / 2 , height() + Cell::SIZE
);
153 // Display the lives label
154 addItem(m_livesLabel
);
155 m_livesLabel
->setPos(width() - m_livesLabel
->boundingRect().width() - Cell::SIZE
, height() + Cell::SIZE
);
156 // Display each Pill and Energizer item and introduction labels
160 GameScene::~GameScene() {
163 for (int i
= 0; i
< m_ghostItems
.size(); ++i
) {
164 delete m_ghostItems
[i
];
166 for (int i
= 0; i
< m_game
->getMaze()->getNbRows();++i
) {
167 for (int j
= 0; j
< m_game
->getMaze()->getNbColumns(); ++j
) {
168 if (m_elementItems
[i
][j
] != NULL
) {
169 delete m_elementItems
[i
][j
];
172 delete[] m_elementItems
[i
];
174 delete[] m_elementItems
;
177 delete m_introLabel2
;
178 delete m_newLevelLabel
;
188 Game
* GameScene::getGame() const {
192 void GameScene::loadTheme() {
193 if (!m_theme
->load(Settings::self()->theme())) {
196 if (!m_renderer
->load(m_theme
->graphics())) {
201 //Update elementIDs, theme properties
203 updateThemeProperties();
205 update(0, 0, width(), height());
207 // Update the theme config: if the default theme is selected, no theme entry is written -> the theme selector does not select the theme
208 Settings::self()->config()->group("General").writeEntry("Theme", Settings::self()->theme());
211 void GameScene::updateSvgIds() {
212 //Needed so new boundingRects() are read for all SVG elements after a theme change
213 // Sanity check, see if game elements already exist
214 if (!m_kapmanItem
) return;
216 // Set the element Id to the right value
217 m_mazeItem
->setElementId("maze");
219 // Create the KapmanItem
220 m_kapmanItem
->setElementId("kapman_0");
221 // Corrects the position of the KapmanItem
222 m_kapmanItem
->update(m_game
->getKapman()->getX(), m_game
->getKapman()->getY());
224 for (int i
= 0; i
< m_ghostItems
.size(); ++i
) {
225 GhostItem
* ghost
= m_ghostItems
[i
];
226 ghost
->setElementId(m_game
->getGhosts()[i
]->getImageId());
227 ghost
->update(m_game
->getGhosts()[i
]->getX(), m_game
->getGhosts()[i
]->getY());
229 for (int i
= 0; i
< m_game
->getMaze()->getNbRows();++i
) {
230 for (int j
= 0; j
< m_game
->getMaze()->getNbColumns(); ++j
) {
231 if (m_elementItems
[i
][j
] != NULL
) {
232 ElementItem
* element
= m_elementItems
[i
][j
];
233 element
->setElementId(m_game
->getMaze()->getCell(i
,j
).getElement()->getImageId());
234 element
->update(m_game
->getMaze()->getCell(i
, j
).getElement()->getX(), m_game
->getMaze()->getCell(i
, j
).getElement()->getY());
240 void GameScene::updateThemeProperties() {
241 // Sanity check, see if game elements already exist
242 if (!m_kapmanItem
) return;
244 // Set the Rotation flag for KapmanItem
245 if (m_theme
->themeProperty("RotateKapman")=="0") {
246 m_kapmanItem
->setRotationFlag(false);
248 m_kapmanItem
->setRotationFlag(true);
252 void GameScene::intro(const bool p_newLevel
) {
253 // If a new level has begun
255 // Set each Pill and Energizer item to its original coordinates
256 for (int i
= 0; i
< m_game
->getMaze()->getNbRows(); ++i
) {
257 for (int j
= 0; j
< m_game
->getMaze()->getNbColumns(); ++j
) {
258 if (m_elementItems
[i
][j
] != NULL
) {
259 if (!items().contains(m_elementItems
[i
][j
])) {
260 addItem(m_elementItems
[i
][j
]);
265 // Display the new level label
266 m_newLevelLabel
->setPlainText(i18nc("The number of the game level", "Level %1", m_game
->getLevel()));
267 if (!items().contains(m_newLevelLabel
)) {
268 addItem(m_newLevelLabel
);
269 m_newLevelLabel
->setPos((width() - m_newLevelLabel
->boundingRect().width()) / 2, (height() - m_newLevelLabel
->boundingRect().height()) / 2);
271 // Display the introduction label
272 if (!items().contains(m_introLabel2
)) {
273 addItem(m_introLabel2
);
274 m_introLabel2
->setPos((width() - m_introLabel2
->boundingRect().width()) / 2,
275 (height() - m_introLabel2
->boundingRect().height() + m_newLevelLabel
->boundingRect().height()) / 2);
278 // Display the introduction labels
279 if (!items().contains(m_introLabel
)) {
280 addItem(m_introLabel
);
281 m_introLabel
->setPos((width() - m_introLabel
->boundingRect().width()) / 2, (height() - m_introLabel
->boundingRect().height()) / 2);
283 if (!items().contains(m_introLabel2
)) {
284 addItem(m_introLabel2
);
285 m_introLabel2
->setPos((width() - m_introLabel2
->boundingRect().width()) / 2,
286 (height() - m_introLabel2
->boundingRect().height() + m_introLabel
->boundingRect().height()) / 2);
291 void GameScene::start() {
292 // If the introduction and new level labels were displayed then remove them
293 if (items().contains(m_introLabel
)) {
294 removeItem(m_introLabel
);
296 if (items().contains(m_introLabel2
)) {
297 removeItem(m_introLabel2
);
299 if (items().contains(m_newLevelLabel
)) {
300 removeItem(m_newLevelLabel
);
304 void GameScene::setPaused(const bool p_pause
, const bool p_fromUser
) {
305 // If the game has paused
307 // If the pause is due to an action from the user
309 // If the label was not displayed yet
310 if (!items().contains(m_pauseLabel
)) {
311 // Display the pause label
312 addItem(m_pauseLabel
);
313 m_pauseLabel
->setPos((width() - m_pauseLabel
->boundingRect().width()) / 2, (height() - m_pauseLabel
->boundingRect().height()) / 2);
316 // Stop kapman animation
317 m_kapmanItem
->pauseAnim();
318 } else { // If the game has resumed
319 // If the pause was due to an action from the user
321 // If the label was displayed
322 if (items().contains(m_pauseLabel
)) {
323 removeItem(m_pauseLabel
);
326 // Resume kapman animation
327 m_kapmanItem
->resumeAnim();
331 void GameScene::hideElement(const qreal p_x
, const qreal p_y
) {
332 if (items().contains(m_elementItems
[(int)((p_y
- (Cell::SIZE
* 0.5)) / Cell::SIZE
)][(int)((p_x
- (Cell::SIZE
* 0.5)) / Cell::SIZE
)])) {
333 removeItem(m_elementItems
[(int)((p_y
- (Cell::SIZE
* 0.5)) / Cell::SIZE
)][(int)((p_x
- (Cell::SIZE
* 0.5)) / Cell::SIZE
)]);
337 void GameScene::displayBonus() {
338 if (!items().contains(m_bonusItem
)) {
339 switch (m_game
->getLevel()) {
341 m_bonusItem
->setElementId("bonus1");
344 m_bonusItem
->setElementId("bonus2");
347 m_bonusItem
->setElementId("bonus3");
350 m_bonusItem
->setElementId("bonus4");
353 m_bonusItem
->setElementId("bonus5");
356 m_bonusItem
->setElementId("bonus6");
359 m_bonusItem
->setElementId("bonus7");
362 m_bonusItem
->update(m_game
->getBonus()->getX(), m_game
->getBonus()->getY());
363 addItem(m_bonusItem
);
367 void GameScene::hideBonus() {
368 if (items().contains(m_bonusItem
)) {
369 removeItem(m_bonusItem
);
373 void GameScene::updateInfo(const Game::InformationTypes p_info
) {
374 if (p_info
& Game::LivesInfo
) {
375 m_livesLabel
->setPlainText(i18n("Lives: %1", m_game
->getLives()));
377 if (p_info
& Game::ScoreInfo
) {
378 m_scoreLabel
->setPlainText(i18n("Score: %1", m_game
->getScore()));
380 if (p_info
& Game::LevelInfo
) {
381 m_levelLabel
->setPlainText(i18nc("The number of the game level", "Level: %1", m_game
->getLevel()));
385 void GameScene::displayPoints(long p_wonPoints
, qreal p_xPos
, qreal p_yPos
) {
386 // Launch a singleShot timer
387 QTimer::singleShot(1000, this, SLOT(hidePoints()));
389 // Add a label in the list of won points Labels
390 m_wonPointsLabels
.prepend(new QGraphicsTextItem(QString::number(p_wonPoints
)));
391 addItem(m_wonPointsLabels
.first());
393 // Temporary reference to the first item in the list
394 QGraphicsTextItem
* tempRef
= m_wonPointsLabels
.first();
396 // Positioning and customization of the point label
397 tempRef
->setDefaultTextColor(QColor("#FFFF00"));
398 tempRef
->setFont(QFont("Helvetica", 15, QFont::Normal
, false));
399 tempRef
->setPos(p_xPos
-(tempRef
->boundingRect().width() / 2), p_yPos
-(tempRef
->boundingRect().height() / 2));
400 tempRef
->setZValue(-1);
403 void GameScene::hidePoints() {
404 // Remove the oldest item of the list from the scene
405 removeItem(m_wonPointsLabels
.last());
406 // Remove the oldest item from the list
407 delete m_wonPointsLabels
.takeLast();