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 | |
26 | GameScene::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 | |
130 | GameScene::~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 | |
154 | Game* GameScene::getGame() const { |
155 | return m_game; |
156 | } |
157 | |
158 | void 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 | |
176 | void 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 | |
205 | void 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 | |
217 | void 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 | |
256 | void 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 | |
269 | void 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 | |
300 | void 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 | |
306 | void 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 | |
336 | void GameScene::hideBonus() { |
337 | if (items().contains(m_bonusItem)) { |
338 | removeItem(m_bonusItem); |
339 | } |
340 | } |
341 | |
342 | |
343 | void 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 | |
361 | void 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 | |