File indexing completed on 2023-10-01 08:02:05
0001 /* 0002 SPDX-FileCopyrightText: 2007-2008 Thomas Gallinari <tg8187@yahoo.fr> 0003 SPDX-FileCopyrightText: 2007-2008 Alexandre Galinier <alex.galinier@hotmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "gamescene.h" 0009 #include "bonus.h" 0010 #include "cell.h" 0011 0012 #include <KgTheme> 0013 0014 #include <KLocalizedString> 0015 0016 GameScene::GameScene(Game *p_game, const KgTheme *theme) 0017 : m_game(p_game) 0018 , m_kapmanItem(nullptr) 0019 , m_mazeItem(nullptr) 0020 { 0021 connect(p_game, &Game::levelStarted, this, &GameScene::intro); 0022 connect(p_game, &Game::gameStarted, this, &GameScene::start); 0023 connect(p_game, &Game::pauseChanged, this, &GameScene::setPaused); 0024 connect(p_game, &Game::elementEaten, this, &GameScene::hideElement); 0025 connect(p_game, &Game::bonusOn, this, &GameScene::displayBonus); 0026 connect(p_game, &Game::bonusOff, this, &GameScene::hideBonus); 0027 0028 // Connection between Game and GameScene for the display of won points when a bonus or a ghost is eaten 0029 connect(p_game, &Game::pointsToDisplay, this, &GameScene::displayPoints); 0030 0031 // Load the SVG file 0032 m_renderer = new QSvgRenderer(); 0033 loadTheme(theme); 0034 0035 // Create the MazeItem 0036 m_mazeItem = new MazeItem(); 0037 // Give the maze the shared renderer to avoid loading the whole SVG file again 0038 m_mazeItem->setSharedRenderer(m_renderer); 0039 // Set the element Id to the right value 0040 m_mazeItem->setElementId(QStringLiteral("maze")); 0041 m_mazeItem->setZValue(-2); 0042 0043 // Create the KapmanItem 0044 m_kapmanItem = new KapmanItem(p_game->getKapman()); 0045 m_kapmanItem->setSharedRenderer(m_renderer); 0046 m_kapmanItem->setElementId(QStringLiteral("kapman_0")); 0047 // Corrects the position of the KapmanItem 0048 m_kapmanItem->update(p_game->getKapman()->getX(), p_game->getKapman()->getY()); 0049 m_kapmanItem->setZValue(2); 0050 // Stops the Kapman animation 0051 m_kapmanItem->stopAnim(); 0052 0053 // Create the GhostItems 0054 for (int i = 0; i < p_game->getGhosts().size(); ++i) { 0055 auto ghost = new GhostItem(p_game->getGhosts()[i]); 0056 ghost->setSharedRenderer(m_renderer); 0057 ghost->setElementId(p_game->getGhosts()[i]->getImageId()); 0058 ghost->update(p_game->getGhosts()[i]->getX(), p_game->getGhosts()[i]->getY()); 0059 // At the beginning, the ghosts are above the kapman because they eat him 0060 ghost->setZValue(3); 0061 m_ghostItems.append(ghost); 0062 } 0063 // Create the Pill and Energizer items 0064 m_elementItems = new ElementItem **[m_game->getMaze()->getNbRows()]; 0065 for (int i = 0; i < m_game->getMaze()->getNbRows(); ++i) { 0066 m_elementItems[i] = new ElementItem *[m_game->getMaze()->getNbColumns()]; 0067 for (int j = 0; j < m_game->getMaze()->getNbColumns(); ++j) { 0068 if (m_game->getMaze()->getCell(i, j).getElement() != nullptr) { 0069 // Create the element and set the image 0070 auto element = new ElementItem(m_game->getMaze()->getCell(i, j).getElement()); 0071 element->setSharedRenderer(m_renderer); 0072 element->setElementId(m_game->getMaze()->getCell(i, j).getElement()->getImageId()); 0073 element->update(m_game->getMaze()->getCell(i, j).getElement()->getX(), m_game->getMaze()->getCell(i, j).getElement()->getY()); 0074 m_elementItems[i][j] = element; 0075 } else { 0076 m_elementItems[i][j] = nullptr; 0077 } 0078 } 0079 } 0080 // Create the Bonus item 0081 m_bonusItem = new ElementItem(m_game->getBonus()); 0082 m_bonusItem->setSharedRenderer(m_renderer); 0083 m_bonusItem->setElementId(QStringLiteral("bonus1")); 0084 0085 // All elements are created, update theme properties 0086 updateSvgIds(); 0087 updateThemeProperties(theme); 0088 0089 // Create the introduction labels 0090 m_introLabel = new QGraphicsTextItem(i18n("GET READY!!!")); 0091 m_introLabel->setFont(QFont(QStringLiteral("Helvetica"), 25, QFont::Bold, false)); 0092 m_introLabel->setDefaultTextColor(QColor(QLatin1String("#FFFF00"))); 0093 m_introLabel->setZValue(4); 0094 m_introLabel2 = new QGraphicsTextItem(i18n("Press any arrow key to start")); 0095 m_introLabel2->setFont(QFont(QStringLiteral("Helvetica"), 15, QFont::Bold, false)); 0096 m_introLabel2->setDefaultTextColor(QColor(QLatin1String("#FFFF00"))); 0097 m_introLabel2->setZValue(4); 0098 // Create the new level label 0099 m_newLevelLabel = new QGraphicsTextItem(); 0100 m_newLevelLabel->setFont(QFont(QStringLiteral("Helvetica"), 35, QFont::Bold, false)); 0101 m_newLevelLabel->setDefaultTextColor(QColor(QLatin1String("#FFFF00"))); 0102 m_newLevelLabel->setZValue(4); 0103 // Create the pause label 0104 m_pauseLabel = new QGraphicsTextItem(i18n("PAUSED")); 0105 m_pauseLabel->setFont(QFont(QStringLiteral("Helvetica"), 35, QFont::Bold, false)); 0106 m_pauseLabel->setDefaultTextColor(QColor(QLatin1String("#FFFF00"))); 0107 m_pauseLabel->setZValue(4); 0108 0109 // Display the MazeItem 0110 addItem(m_mazeItem); 0111 // Display the KapmanItem 0112 addItem(m_kapmanItem); 0113 // Display each GhostItem 0114 for (int i = 0; i < m_ghostItems.size(); ++i) { 0115 addItem(m_ghostItems[i]); 0116 } 0117 // Display each Pill and Energizer item and introduction labels 0118 intro(true); 0119 } 0120 0121 GameScene::~GameScene() 0122 { 0123 delete m_mazeItem; 0124 delete m_kapmanItem; 0125 for (int i = 0; i < m_ghostItems.size(); ++i) { 0126 delete m_ghostItems[i]; 0127 } 0128 for (int i = 0; i < m_game->getMaze()->getNbRows(); ++i) { 0129 for (int j = 0; j < m_game->getMaze()->getNbColumns(); ++j) { 0130 if (m_elementItems[i][j] != nullptr) { 0131 delete m_elementItems[i][j]; 0132 } 0133 } 0134 delete[] m_elementItems[i]; 0135 } 0136 delete[] m_elementItems; 0137 delete m_bonusItem; 0138 delete m_introLabel; 0139 delete m_introLabel2; 0140 delete m_newLevelLabel; 0141 delete m_pauseLabel; 0142 delete m_renderer; 0143 } 0144 0145 Game *GameScene::getGame() const 0146 { 0147 return m_game; 0148 } 0149 0150 void GameScene::loadTheme(const KgTheme *theme) 0151 { 0152 if (!m_renderer->load(theme->graphicsPath())) { 0153 return; 0154 } 0155 0156 // Update elementIDs, theme properties 0157 updateSvgIds(); 0158 updateThemeProperties(theme); 0159 0160 update(0, 0, width(), height()); 0161 } 0162 0163 void GameScene::updateSvgIds() 0164 { 0165 // Needed so new boundingRects() are read for all SVG elements after a theme change 0166 // Sanity check, see if game elements already exist 0167 if (!m_kapmanItem) { 0168 return; 0169 } 0170 0171 // Set the element Id to the right value 0172 m_mazeItem->setElementId(QStringLiteral("maze")); 0173 0174 // Create the KapmanItem 0175 m_kapmanItem->setElementId(QStringLiteral("kapman_0")); 0176 // Corrects the position of the KapmanItem 0177 m_kapmanItem->update(m_game->getKapman()->getX(), m_game->getKapman()->getY()); 0178 0179 for (int i = 0; i < m_ghostItems.size(); ++i) { 0180 GhostItem *ghost = m_ghostItems[i]; 0181 ghost->setElementId(m_game->getGhosts()[i]->getImageId()); 0182 ghost->update(m_game->getGhosts()[i]->getX(), m_game->getGhosts()[i]->getY()); 0183 } 0184 for (int i = 0; i < m_game->getMaze()->getNbRows(); ++i) { 0185 for (int j = 0; j < m_game->getMaze()->getNbColumns(); ++j) { 0186 if (m_elementItems[i][j] != nullptr) { 0187 ElementItem *element = m_elementItems[i][j]; 0188 element->setElementId(m_game->getMaze()->getCell(i, j).getElement()->getImageId()); 0189 element->update(m_game->getMaze()->getCell(i, j).getElement()->getX(), m_game->getMaze()->getCell(i, j).getElement()->getY()); 0190 } 0191 } 0192 } 0193 } 0194 0195 void GameScene::updateThemeProperties(const KgTheme *theme) 0196 { 0197 // Sanity check, see if game elements already exist 0198 if (!m_kapmanItem) { 0199 return; 0200 } 0201 0202 // Set the Rotation flag for KapmanItem 0203 if (theme->customData(QStringLiteral("RotateKapman")) == QLatin1String("0")) { 0204 m_kapmanItem->setRotationFlag(false); 0205 } else { 0206 m_kapmanItem->setRotationFlag(true); 0207 } 0208 } 0209 0210 void GameScene::intro(const bool p_newLevel) 0211 { 0212 // If a new level has begun 0213 if (p_newLevel) { 0214 // Set each Pill and Energizer item to its original coordinates 0215 for (int i = 0; i < m_game->getMaze()->getNbRows(); ++i) { 0216 for (int j = 0; j < m_game->getMaze()->getNbColumns(); ++j) { 0217 if (m_elementItems[i][j] != nullptr) { 0218 if (!items().contains(m_elementItems[i][j])) { 0219 addItem(m_elementItems[i][j]); 0220 } 0221 } 0222 } 0223 } 0224 if (items().contains(m_introLabel)) { 0225 removeItem(m_introLabel); 0226 } 0227 // Display the new level label 0228 m_newLevelLabel->setPlainText(i18nc("The number of the game level", "Level %1", m_game->getLevel())); 0229 if (!items().contains(m_newLevelLabel)) { 0230 addItem(m_newLevelLabel); 0231 m_newLevelLabel->setPos((width() - m_newLevelLabel->boundingRect().width()) / 2, (height() - m_newLevelLabel->boundingRect().height()) / 2); 0232 } 0233 // Display the introduction label 0234 if (!items().contains(m_introLabel2)) { 0235 addItem(m_introLabel2); 0236 m_introLabel2->setPos((width() - m_introLabel2->boundingRect().width()) / 2, 0237 (height() - m_introLabel2->boundingRect().height() + m_newLevelLabel->boundingRect().height()) / 2); 0238 } 0239 } else { 0240 // Display the introduction labels 0241 if (!items().contains(m_introLabel)) { 0242 addItem(m_introLabel); 0243 m_introLabel->setPos((width() - m_introLabel->boundingRect().width()) / 2, (height() - m_introLabel->boundingRect().height()) / 2); 0244 } 0245 if (!items().contains(m_introLabel2)) { 0246 addItem(m_introLabel2); 0247 m_introLabel2->setPos((width() - m_introLabel2->boundingRect().width()) / 2, 0248 (height() - m_introLabel2->boundingRect().height() + m_introLabel->boundingRect().height()) / 2); 0249 } 0250 } 0251 } 0252 0253 void GameScene::start() 0254 { 0255 // If the introduction and new level labels were displayed then remove them 0256 if (items().contains(m_introLabel)) { 0257 removeItem(m_introLabel); 0258 } 0259 if (items().contains(m_introLabel2)) { 0260 removeItem(m_introLabel2); 0261 } 0262 if (items().contains(m_newLevelLabel)) { 0263 removeItem(m_newLevelLabel); 0264 } 0265 } 0266 0267 void GameScene::setPaused(const bool p_pause, const bool p_fromUser) 0268 { 0269 // If the game has paused 0270 if (p_pause) { 0271 // If the pause is due to an action from the user 0272 if (p_fromUser) { 0273 // If the label was not displayed yet 0274 if (!items().contains(m_pauseLabel)) { 0275 // FIXME: Hack to remove labels when pausing game while init labels are shown (icwiener) 0276 // This should be done cleaner 0277 // FIXME #2: start() is a misleading method name ... 0278 start(); 0279 // Display the pause label 0280 addItem(m_pauseLabel); 0281 m_pauseLabel->setPos((width() - m_pauseLabel->boundingRect().width()) / 2, (height() - m_pauseLabel->boundingRect().height()) / 2); 0282 } 0283 } 0284 // Stop kapman animation 0285 m_kapmanItem->pauseAnim(); 0286 } else { // If the game has resumed 0287 // If the pause was due to an action from the user 0288 if (p_fromUser) { 0289 // If the label was displayed 0290 if (items().contains(m_pauseLabel)) { 0291 removeItem(m_pauseLabel); 0292 } 0293 } 0294 // Resume kapman animation 0295 m_kapmanItem->resumeAnim(); 0296 } 0297 } 0298 0299 void GameScene::hideElement(const qreal p_x, const qreal p_y) 0300 { 0301 if (items().contains(m_elementItems[(int)((p_y - (Cell::SIZE * 0.5)) / Cell::SIZE)][(int)((p_x - (Cell::SIZE * 0.5)) / Cell::SIZE)])) { 0302 removeItem(m_elementItems[(int)((p_y - (Cell::SIZE * 0.5)) / Cell::SIZE)][(int)((p_x - (Cell::SIZE * 0.5)) / Cell::SIZE)]); 0303 } 0304 } 0305 0306 void GameScene::displayBonus() 0307 { 0308 if (!items().contains(m_bonusItem)) { 0309 switch (m_game->getLevel()) { 0310 case 1: 0311 m_bonusItem->setElementId(QStringLiteral("bonus1")); 0312 break; 0313 case 2: 0314 m_bonusItem->setElementId(QStringLiteral("bonus2")); 0315 break; 0316 case 3: 0317 m_bonusItem->setElementId(QStringLiteral("bonus3")); 0318 break; 0319 case 4: 0320 m_bonusItem->setElementId(QStringLiteral("bonus4")); 0321 break; 0322 case 5: 0323 m_bonusItem->setElementId(QStringLiteral("bonus5")); 0324 break; 0325 case 6: 0326 m_bonusItem->setElementId(QStringLiteral("bonus6")); 0327 break; 0328 default: 0329 m_bonusItem->setElementId(QStringLiteral("bonus7")); 0330 break; 0331 } 0332 m_bonusItem->update(m_game->getBonus()->getX(), m_game->getBonus()->getY()); 0333 addItem(m_bonusItem); 0334 } 0335 } 0336 0337 void GameScene::hideBonus() 0338 { 0339 if (items().contains(m_bonusItem)) { 0340 removeItem(m_bonusItem); 0341 } 0342 } 0343 0344 void GameScene::displayPoints(long p_wonPoints, qreal p_xPos, qreal p_yPos) 0345 { 0346 // Launch a singleShot timer 0347 QTimer::singleShot(1000, this, &GameScene::hidePoints); 0348 0349 // Add a label in the list of won points Labels 0350 m_wonPointsLabels.prepend(new QGraphicsTextItem(QString::number(p_wonPoints))); 0351 addItem(m_wonPointsLabels.first()); 0352 0353 // Temporary reference to the first item in the list 0354 QGraphicsTextItem *tempRef = m_wonPointsLabels.first(); 0355 0356 // Positioning and customization of the point label 0357 tempRef->setDefaultTextColor(QColor(QLatin1String("#FFFF00"))); 0358 tempRef->setFont(QFont(QStringLiteral("Helvetica"), 15, QFont::Normal, false)); 0359 tempRef->setPos(p_xPos - (tempRef->boundingRect().width() / 2), p_yPos - (tempRef->boundingRect().height() / 2)); 0360 tempRef->setZValue(-1); 0361 } 0362 0363 void GameScene::hidePoints() 0364 { 0365 // Remove the oldest item of the list from the scene 0366 removeItem(m_wonPointsLabels.last()); 0367 // Remove the oldest item from the list 0368 delete m_wonPointsLabels.takeLast(); 0369 } 0370 0371 #include "moc_gamescene.cpp"