1/*
2 * Copyright 2007-2008 Thomas Gallinari <tg8187@yahoo.fr>
3 * Copyright 2007-2008 Alexandre Galinier <alex.galinier@hotmail.com>
4 *
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.
9 *
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.
14 *
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/>.
17 */
18
19#include "gamescene.h"
20#include "cell.h"
21#include "bonus.h"
22#include "settings.h"
23
24#include <KLocale>
25
26GameScene::GameScene(Game* p_game) : m_game(p_game), m_kapmanItem(0), m_mazeItem(0) {
27 connect(p_game, SIGNAL(levelStarted(bool)), SLOT(intro(bool)));
28 connect(p_game, SIGNAL(gameStarted()), this, SLOT(start()));
29 connect(p_game, SIGNAL(pauseChanged(bool,bool)), this, SLOT(setPaused(bool,bool)));
30 connect(p_game, SIGNAL(elementEaten(qreal,qreal)), this, SLOT(hideElement(qreal,qreal)));
31 connect(p_game, SIGNAL(bonusOn()), this, SLOT(displayBonus()));
32 connect(p_game, SIGNAL(bonusOff()), this, SLOT(hideBonus()));
33
34 // Connection between Game and GameScene for the display of won points when a bonus or a ghost is eaten
35 connect(p_game, SIGNAL(pointsToDisplay(long,qreal,qreal)), this, SLOT(displayPoints(long,qreal,qreal)));
36
37 // Create the theme instance
38 m_theme = new KGameTheme();
39
40 // Load the SVG file
41 m_renderer = new QSvgRenderer();
42 loadTheme();
43
44 // Create the MazeItem
45 m_mazeItem = new MazeItem();
46 // Give the maze the shared renderer to avoid loading the whole SVG file again
47 m_mazeItem->setSharedRenderer(m_renderer);
48 // Set the element Id to the right value
49 m_mazeItem->setElementId("maze");
50 m_mazeItem->setZValue(-2);
51
52 // Create the KapmanItem
53 m_kapmanItem = new KapmanItem(p_game->getKapman());
54 m_kapmanItem->setSharedRenderer(m_renderer);
55 m_kapmanItem->setElementId("kapman_0");
56 // Corrects the position of the KapmanItem
57 m_kapmanItem->update(p_game->getKapman()->getX(), p_game->getKapman()->getY());
58 m_kapmanItem->setZValue(2);
59 // Stops the Kapman animation
60 m_kapmanItem->stopAnim();
61
62 // Create the GhostItems
63 for (int i = 0; i < p_game->getGhosts().size(); ++i) {
64 GhostItem* ghost = new GhostItem(p_game->getGhosts()[i]);
65 ghost->setSharedRenderer(m_renderer);
66 ghost->setElementId(p_game->getGhosts()[i]->getImageId());
67 ghost->update(p_game->getGhosts()[i]->getX(), p_game->getGhosts()[i]->getY());
68 // At the beginning, the ghosts are above the kapman because they eat him
69 ghost->setZValue(3);
70 m_ghostItems.append(ghost);
71 }
72 // Create the Pill and Energizer items
73 m_elementItems = new ElementItem**[m_game->getMaze()->getNbRows()];
74 for (int i = 0; i < m_game->getMaze()->getNbRows(); ++i) {
75 m_elementItems[i] = new ElementItem*[m_game->getMaze()->getNbColumns()];
76 for (int j = 0; j < m_game->getMaze()->getNbColumns(); ++j) {
77 if (m_game->getMaze()->getCell(i, j).getElement() != NULL) {
78 // Create the element and set the image
79 ElementItem* element = new ElementItem(m_game->getMaze()->getCell(i, j).getElement());
80 element->setSharedRenderer(m_renderer);
81 element->setElementId(m_game->getMaze()->getCell(i,j).getElement()->getImageId());
82 element->update(m_game->getMaze()->getCell(i, j).getElement()->getX(), m_game->getMaze()->getCell(i, j).getElement()->getY());
83 m_elementItems[i][j] = element;
84 } else {
85 m_elementItems[i][j] = NULL;
86 }
87 }
88 }
89 // Create the Bonus item
90 m_bonusItem = new ElementItem(m_game->getBonus());
91 m_bonusItem->setSharedRenderer(m_renderer);
92 m_bonusItem->setElementId("bonus1");
93
94 // All elements are created, update theme properties
95 updateSvgIds();
96 updateThemeProperties();
97
98 // Create the introduction labels
99 m_introLabel = new QGraphicsTextItem(i18n("GET READY!!!"));
100 m_introLabel->setFont(QFont("Helvetica", 25, QFont::Bold, false));
101 m_introLabel->setDefaultTextColor(QColor("#FFFF00"));
102 m_introLabel->setZValue(4);
103 m_introLabel2 = new QGraphicsTextItem(i18n("Press any arrow key to start"));
104 m_introLabel2->setFont(QFont("Helvetica", 15, QFont::Bold, false));
105 m_introLabel2->setDefaultTextColor(QColor("#FFFF00"));
106 m_introLabel2->setZValue(4);
107 // Create the new level label
108 m_newLevelLabel = new QGraphicsTextItem();
109 m_newLevelLabel->setFont(QFont("Helvetica", 35, QFont::Bold, false));
110 m_newLevelLabel->setDefaultTextColor(QColor("#FFFF00"));
111 m_newLevelLabel->setZValue(4);
112 // Create the pause label
113 m_pauseLabel = new QGraphicsTextItem(i18n("PAUSED"));
114 m_pauseLabel->setFont(QFont("Helvetica", 35, QFont::Bold, false));
115 m_pauseLabel->setDefaultTextColor(QColor("#FFFF00"));
116 m_pauseLabel->setZValue(4);
117
118 // Display the MazeItem
119 addItem(m_mazeItem);
120 // Display the KapmanItem
121 addItem(m_kapmanItem);
122 // Display each GhostItem
123 for (int i = 0; i < m_ghostItems.size(); ++i) {
124 addItem(m_ghostItems[i]);
125 }
126 // Display each Pill and Energizer item and introduction labels
127 intro(true);
128}
129
130GameScene::~GameScene() {
131 delete m_mazeItem;
132 delete m_kapmanItem;
133 for (int i = 0; i < m_ghostItems.size(); ++i) {
134 delete m_ghostItems[i];
135 }
136 for (int i = 0; i < m_game->getMaze()->getNbRows();++i) {
137 for (int j = 0; j < m_game->getMaze()->getNbColumns(); ++j) {
138 if (m_elementItems[i][j] != NULL) {
139 delete m_elementItems[i][j];
140 }
141 }
142 delete[] m_elementItems[i];
143 }
144 delete[] m_elementItems;
145 delete m_bonusItem;
146 delete m_introLabel;
147 delete m_introLabel2;
148 delete m_newLevelLabel;
149 delete m_pauseLabel;
150 delete m_renderer;
151 delete m_theme;
152}
153
154Game* GameScene::getGame() const {
155 return m_game;
156}
157
158void GameScene::loadTheme() {
159 if (!m_theme->load(Settings::self()->theme())) {
160 return;
161 }
162 if (!m_renderer->load(m_theme->graphics())) {
163 return;
164 }
165
166 //Update elementIDs, theme properties
167 updateSvgIds();
168 updateThemeProperties();
169
170 update(0, 0, width(), height());
171
172 // Update the theme config: if the default theme is selected, no theme entry is written -> the theme selector does not select the theme
173 Settings::self()->config()->group("General").writeEntry("Theme", Settings::self()->theme());
174}
175
176void GameScene::updateSvgIds() {
177 //Needed so new boundingRects() are read for all SVG elements after a theme change
178 // Sanity check, see if game elements already exist
179 if (!m_kapmanItem) return;
180
181 // Set the element Id to the right value
182 m_mazeItem->setElementId("maze");
183
184 // Create the KapmanItem
185 m_kapmanItem->setElementId("kapman_0");
186 // Corrects the position of the KapmanItem
187 m_kapmanItem->update(m_game->getKapman()->getX(), m_game->getKapman()->getY());
188
189 for (int i = 0; i < m_ghostItems.size(); ++i) {
190 GhostItem* ghost = m_ghostItems[i];
191 ghost->setElementId(m_game->getGhosts()[i]->getImageId());
192 ghost->update(m_game->getGhosts()[i]->getX(), m_game->getGhosts()[i]->getY());
193 }
194 for (int i = 0; i < m_game->getMaze()->getNbRows();++i) {
195 for (int j = 0; j < m_game->getMaze()->getNbColumns(); ++j) {
196 if (m_elementItems[i][j] != NULL) {
197 ElementItem* element = m_elementItems[i][j];
198 element->setElementId(m_game->getMaze()->getCell(i,j).getElement()->getImageId());
199 element->update(m_game->getMaze()->getCell(i, j).getElement()->getX(), m_game->getMaze()->getCell(i, j).getElement()->getY());
200 }
201 }
202 }
203}
204
205void GameScene::updateThemeProperties() {
206 // Sanity check, see if game elements already exist
207 if (!m_kapmanItem) return;
208
209 // Set the Rotation flag for KapmanItem
210 if (m_theme->themeProperty("RotateKapman")=="0") {
211 m_kapmanItem->setRotationFlag(false);
212 } else {
213 m_kapmanItem->setRotationFlag(true);
214 }
215}
216
217void GameScene::intro(const bool p_newLevel) {
218 // If a new level has begun
219 if (p_newLevel) {
220 // Set each Pill and Energizer item to its original coordinates
221 for (int i = 0; i < m_game->getMaze()->getNbRows(); ++i) {
222 for (int j = 0; j < m_game->getMaze()->getNbColumns(); ++j) {
223 if (m_elementItems[i][j] != NULL) {
224 if (!items().contains(m_elementItems[i][j])) {
225 addItem(m_elementItems[i][j]);
226 }
227 }
228 }
229 }
230 // Display the new level label
231 m_newLevelLabel->setPlainText(i18nc("The number of the game level", "Level %1", m_game->getLevel()));
232 if (!items().contains(m_newLevelLabel)) {
233 addItem(m_newLevelLabel);
234 m_newLevelLabel->setPos((width() - m_newLevelLabel->boundingRect().width()) / 2, (height() - m_newLevelLabel->boundingRect().height()) / 2);
235 }
236 // Display the introduction label
237 if (!items().contains(m_introLabel2)) {
238 addItem(m_introLabel2);
239 m_introLabel2->setPos((width() - m_introLabel2->boundingRect().width()) / 2,
240 (height() - m_introLabel2->boundingRect().height() + m_newLevelLabel->boundingRect().height()) / 2);
241 }
242 } else {
243 // Display the introduction labels
244 if (!items().contains(m_introLabel)) {
245 addItem(m_introLabel);
246 m_introLabel->setPos((width() - m_introLabel->boundingRect().width()) / 2, (height() - m_introLabel->boundingRect().height()) / 2);
247 }
248 if (!items().contains(m_introLabel2)) {
249 addItem(m_introLabel2);
250 m_introLabel2->setPos((width() - m_introLabel2->boundingRect().width()) / 2,
251 (height() - m_introLabel2->boundingRect().height() + m_introLabel->boundingRect().height()) / 2);
252 }
253 }
254}
255
256void GameScene::start() {
257 // If the introduction and new level labels were displayed then remove them
258 if (items().contains(m_introLabel)) {
259 removeItem(m_introLabel);
260 }
261 if (items().contains(m_introLabel2)) {
262 removeItem(m_introLabel2);
263 }
264 if (items().contains(m_newLevelLabel)) {
265 removeItem(m_newLevelLabel);
266 }
267}
268
269void GameScene::setPaused(const bool p_pause, const bool p_fromUser) {
270 // If the game has paused
271 if (p_pause) {
272 // If the pause is due to an action from the user
273 if (p_fromUser) {
274 // If the label was not displayed yet
275 if (!items().contains(m_pauseLabel)) {
276 // FIXME: Hack to remove labels when pausing game while init labels are shown (icwiener)
277 // This should be done cleaner
278 // FIXME #2: start() is a misleading method name ...
279 start();
280 // Display the pause label
281 addItem(m_pauseLabel);
282 m_pauseLabel->setPos((width() - m_pauseLabel->boundingRect().width()) / 2, (height() - m_pauseLabel->boundingRect().height()) / 2);
283 }
284 }
285 // Stop kapman animation
286 m_kapmanItem->pauseAnim();
287 } else { // If the game has resumed
288 // If the pause was due to an action from the user
289 if (p_fromUser) {
290 // If the label was displayed
291 if (items().contains(m_pauseLabel)) {
292 removeItem(m_pauseLabel);
293 }
294 }
295 // Resume kapman animation
296 m_kapmanItem->resumeAnim();
297 }
298}
299
300void GameScene::hideElement(const qreal p_x, const qreal p_y) {
301 if (items().contains(m_elementItems[(int)((p_y - (Cell::SIZE * 0.5)) / Cell::SIZE)][(int)((p_x - (Cell::SIZE * 0.5)) / Cell::SIZE)])) {
302 removeItem(m_elementItems[(int)((p_y - (Cell::SIZE * 0.5)) / Cell::SIZE)][(int)((p_x - (Cell::SIZE * 0.5)) / Cell::SIZE)]);
303 }
304}
305
306void GameScene::displayBonus() {
307 if (!items().contains(m_bonusItem)) {
308 switch (m_game->getLevel()) {
309 case 1:
310 m_bonusItem->setElementId("bonus1");
311 break;
312 case 2:
313 m_bonusItem->setElementId("bonus2");
314 break;
315 case 3:
316 m_bonusItem->setElementId("bonus3");
317 break;
318 case 4:
319 m_bonusItem->setElementId("bonus4");
320 break;
321 case 5:
322 m_bonusItem->setElementId("bonus5");
323 break;
324 case 6:
325 m_bonusItem->setElementId("bonus6");
326 break;
327 default:
328 m_bonusItem->setElementId("bonus7");
329 break;
330 }
331 m_bonusItem->update(m_game->getBonus()->getX(), m_game->getBonus()->getY());
332 addItem(m_bonusItem);
333 }
334}
335
336void GameScene::hideBonus() {
337 if (items().contains(m_bonusItem)) {
338 removeItem(m_bonusItem);
339 }
340}
341
342
343void GameScene::displayPoints(long p_wonPoints, qreal p_xPos, qreal p_yPos) {
344 // Launch a singleShot timer
345 QTimer::singleShot(1000, this, SLOT(hidePoints()));
346
347 // Add a label in the list of won points Labels
348 m_wonPointsLabels.prepend(new QGraphicsTextItem(QString::number(p_wonPoints)));
349 addItem(m_wonPointsLabels.first());
350
351 // Temporary reference to the first item in the list
352 QGraphicsTextItem* tempRef = m_wonPointsLabels.first();
353
354 // Positioning and customization of the point label
355 tempRef->setDefaultTextColor(QColor("#FFFF00"));
356 tempRef->setFont(QFont("Helvetica", 15, QFont::Normal, false));
357 tempRef->setPos(p_xPos-(tempRef->boundingRect().width() / 2), p_yPos-(tempRef->boundingRect().height() / 2));
358 tempRef->setZValue(-1);
359}
360
361void GameScene::hidePoints() {
362 // Remove the oldest item of the list from the scene
363 removeItem(m_wonPointsLabels.last());
364 // Remove the oldest item from the list
365 delete m_wonPointsLabels.takeLast();
366}
367