File indexing completed on 2023-12-03 07:52:32
0001 /* 0002 SPDX-FileCopyrightText: 2009 Mathias Kraus <k.hias@gmx.de> 0003 SPDX-FileCopyrightText: 2007-2008 Thomas Gallinari <tg8187@yahoo.fr> 0004 SPDX-FileCopyrightText: 2007-2008 Alexandre Galinier <alex.galinier@hotmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "gamescene.h" 0010 #include "cell.h" 0011 #include "bonus.h" 0012 #include "bonusitem.h" 0013 #include "settings.h" 0014 #include "game.h" 0015 #include "player.h" 0016 #include "block.h" 0017 #include "blockitem.h" 0018 #include "bomb.h" 0019 #include "arena.h" 0020 #include "arenaitem.h" 0021 #include "playeritem.h" 0022 #include "bombitem.h" 0023 #include "bombexplosionitem.h" 0024 #include "infooverlay.h" 0025 #include "infosidebar.h" 0026 #include "granatier_random.h" 0027 0028 #include <KgTheme> 0029 #include <KgThemeProvider> 0030 #include <KLocalizedString> 0031 0032 #include <QGraphicsView> 0033 #include <QTimer> 0034 0035 #include <KGameRenderer> 0036 #include <KGameRenderedItem> 0037 0038 #include <cmath> 0039 #include <QStandardPaths> 0040 0041 GameScene::GameScene(Game* p_game, KgThemeProvider* p_themeProvider) : m_game(p_game), m_themeProvider(p_themeProvider) 0042 { 0043 connect(p_game, &Game::gameStarted, this, &GameScene::start); 0044 connect(p_game, &Game::pauseChanged, this, &GameScene::setPaused); 0045 connect(p_game, &Game::bombCreated, this, &GameScene::createBombItem); 0046 connect(p_game, &Game::infoChanged, this, &GameScene::updateInfo); 0047 0048 m_SvgScaleFactor = 1; 0049 0050 m_backgroundResizeTimer = new QTimer(); 0051 m_backgroundResizeTimer->setSingleShot(true); 0052 connect(m_backgroundResizeTimer, &QTimer::timeout, this, &GameScene::resizeBackground); 0053 0054 // Create the PlayerItems and the points labels 0055 QList <Player*> players = p_game->getPlayers(); 0056 0057 PlayerItem* playerItem; 0058 for(auto & player : players) 0059 { 0060 const QString desktopPath = player->getDesktopFilePath(); 0061 auto* theme = new KgTheme(desktopPath.toUtf8()); 0062 theme->readFromDesktopFile(desktopPath); 0063 auto playerRenderer = new KGameRenderer(theme); 0064 m_mapRendererPlayerItems.insert(player, playerRenderer); 0065 playerItem = new PlayerItem(player, playerRenderer); 0066 // Corrects the position of the player 0067 playerItem->update(player->getX(), player->getY()); 0068 // Stops the player animation 0069 playerItem->stopAnim(); 0070 0071 m_playerItems.append(playerItem); 0072 0073 connect(this, &GameScene::resizeGraphics, playerItem, &PlayerItem::updateGraphics); 0074 connect(playerItem, &PlayerItem::bonusItemTaken, this, &GameScene::removeBonusItem); 0075 } 0076 0077 // The remaining time 0078 m_remainingTimeLabel = new QGraphicsTextItem(i18n("0:00")); 0079 m_remainingTimeLabel->setFont(QFont(QStringLiteral("Helvetica"), static_cast<int>(Granatier::CellSize * 0.35), QFont::Bold, false)); 0080 m_remainingTimeLabel->setDefaultTextColor(QColor("#FFFF00")); 0081 m_remainingTimeLabel->setZValue(1000); 0082 0083 m_arenaNameLabel = new QGraphicsTextItem(i18n("Arena Name")); 0084 m_arenaNameLabel->setFont(QFont(QStringLiteral("Helvetica"), static_cast<int>(Granatier::CellSize * 0.35), QFont::Bold, false)); 0085 m_arenaNameLabel->setDefaultTextColor(QColor("#FFFF00")); 0086 m_arenaNameLabel->setZValue(1000); 0087 0088 // setup the theme renderer 0089 m_rendererSelectedTheme = new KGameRenderer(m_themeProvider); 0090 m_rendererDefaultTheme = nullptr; 0091 setupThemeRenderer(); 0092 0093 connect(m_themeProvider, &KgThemeProvider::currentThemeChanged, this, &GameScene::themeChanged); 0094 0095 // create the info overlay 0096 m_infoOverlay = new InfoOverlay(m_game, this); 0097 connect(this, &GameScene::resizeGraphics, m_infoOverlay, &InfoOverlay::updateGraphics); 0098 0099 init(); 0100 } 0101 0102 void GameScene::setupThemeRenderer() 0103 { 0104 bool selectedThemeIsDefault = true; 0105 0106 if(m_themeProvider->currentTheme() != m_themeProvider->defaultTheme()) 0107 { 0108 // Load the default SVG file as fallback 0109 selectedThemeIsDefault = false; 0110 if(m_rendererDefaultTheme == nullptr) 0111 { 0112 auto* theme = new KgTheme(m_themeProvider->defaultTheme()->identifier()); 0113 theme->setGraphicsPath(m_themeProvider->defaultTheme()->graphicsPath()); 0114 m_rendererDefaultTheme = new KGameRenderer(theme); 0115 } 0116 } 0117 0118 if(selectedThemeIsDefault || m_rendererSelectedTheme->spriteExists(QStringLiteral("background"))) 0119 { 0120 m_rendererBackground = m_rendererSelectedTheme; 0121 } 0122 else 0123 { 0124 m_rendererBackground = m_rendererDefaultTheme; 0125 } 0126 0127 // set the renderer for the arena items TODO: add all the arena items 0128 if(selectedThemeIsDefault || (m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_ground")) && 0129 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_wall")) && 0130 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_block")) && 0131 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_block_highlight")) && 0132 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_ice")) && 0133 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_bomb_mortar")) && 0134 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_arrow_up")) && 0135 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_arrow_right")) && 0136 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_arrow_down")) && 0137 m_rendererSelectedTheme->spriteExists(QStringLiteral("arena_arrow_left")))) 0138 { 0139 m_rendererArenaItems = m_rendererSelectedTheme; 0140 } 0141 else 0142 { 0143 m_rendererArenaItems = m_rendererDefaultTheme; 0144 } 0145 0146 // set the renderer for the bonus items TODO: add all the bonus items 0147 if(selectedThemeIsDefault || (m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_speed")) && 0148 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_bomb")) && 0149 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_power")) && 0150 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_shield")) && 0151 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_throw")) && 0152 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_kick")) && 0153 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_bad_slow")) && 0154 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_bad_hyperactive")) && 0155 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_bad_mirror")) && 0156 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_bad_scatty")) && 0157 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_bad_restrain")) && 0158 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_neutral_pandora")) && 0159 m_rendererSelectedTheme->spriteExists(QStringLiteral("bonus_neutral_resurrect")))) 0160 { 0161 m_rendererBonusItems = m_rendererSelectedTheme; 0162 } 0163 else 0164 { 0165 m_rendererBonusItems = m_rendererDefaultTheme; 0166 } 0167 0168 // set the renderer for the bomb items 0169 if(selectedThemeIsDefault || (m_rendererSelectedTheme->spriteExists(QStringLiteral("bomb")) && 0170 m_rendererSelectedTheme->spriteExists(QStringLiteral("bomb_blast_core_0")) && 0171 m_rendererSelectedTheme->spriteExists(QStringLiteral("bomb_blast_north_0")) && 0172 m_rendererSelectedTheme->spriteExists(QStringLiteral("bomb_blast_east_0")) && 0173 m_rendererSelectedTheme->spriteExists(QStringLiteral("bomb_blast_south_0")) && 0174 m_rendererSelectedTheme->spriteExists(QStringLiteral("bomb_blast_west_0")))) 0175 { 0176 m_rendererBombItems = m_rendererSelectedTheme; 0177 } 0178 else 0179 { 0180 m_rendererBombItems = m_rendererDefaultTheme; 0181 } 0182 0183 // set the renderer for the score items 0184 if(selectedThemeIsDefault || (m_rendererSelectedTheme->spriteExists(QStringLiteral("score_star_enabled")) && 0185 m_rendererSelectedTheme->spriteExists(QStringLiteral("score_star_disabled")))) 0186 { 0187 m_rendererScoreItems = m_rendererSelectedTheme; 0188 } 0189 else 0190 { 0191 m_rendererScoreItems = m_rendererDefaultTheme; 0192 } 0193 } 0194 0195 void GameScene::init() 0196 { 0197 initItemsWithGraphicsFromTheme(); 0198 0199 // Display each PlayerItem 0200 for (auto & playerItem : m_playerItems) 0201 { 0202 if(!items().contains(playerItem)) 0203 { 0204 addItem(playerItem); 0205 } 0206 playerItem->resurrect(); 0207 } 0208 0209 if (!items().contains(m_remainingTimeLabel)) 0210 { 0211 addItem(m_remainingTimeLabel); 0212 } 0213 m_remainingTimeLabel->setDefaultTextColor(QColor("#FFFF00")); 0214 int nTime = m_game->getRemainingTime(); 0215 m_remainingTimeLabel->setPlainText(QStringLiteral("%1:%2").arg(nTime/60).arg(nTime%60, 2, 10, QLatin1Char('0'))); 0216 m_remainingTimeLabel->setPos(Granatier::CellSize * m_game->getArena()->getNbColumns() - m_remainingTimeLabel->boundingRect().width(), - m_remainingTimeLabel->boundingRect().height()); 0217 0218 if (!items().contains(m_arenaNameLabel)) 0219 { 0220 addItem(m_arenaNameLabel); 0221 } 0222 m_arenaNameLabel->setPlainText(m_game->getArena()->getName()); 0223 m_arenaNameLabel->setPos(0, - m_arenaNameLabel->boundingRect().height()); 0224 0225 //this is needed for info sidebar 0226 setSceneRect(0, -m_remainingTimeLabel->boundingRect().height(), 0227 m_game->getArena()->getNbColumns()*Granatier::CellSize, 0228 m_game->getArena()->getNbRows()*Granatier::CellSize + m_remainingTimeLabel->boundingRect().height()); 0229 0230 // create the info sidebar 0231 m_infoSidebar = new InfoSidebar(m_game, this); 0232 connect(this, &GameScene::resizeGraphics, m_infoSidebar, &InfoSidebar::updateGraphics); 0233 0234 //update the sceneRect 0235 QRectF oldSceneRect = sceneRect(); 0236 QRectF sidebarRect = m_infoSidebar->rect(); 0237 QRectF newSceneRect; 0238 newSceneRect.setLeft(oldSceneRect.left() < sidebarRect.left() ? oldSceneRect.left() : sidebarRect.left()); 0239 newSceneRect.setRight(oldSceneRect.right() > sidebarRect.right() ? oldSceneRect.right() : sidebarRect.right()); 0240 newSceneRect.setTop(oldSceneRect.top() < sidebarRect.top() ? oldSceneRect.top() : sidebarRect.top()); 0241 newSceneRect.setBottom(oldSceneRect.bottom() > sidebarRect.bottom() ? oldSceneRect.bottom() : sidebarRect.bottom()); 0242 newSceneRect.adjust(-5, -5, 5, 5); 0243 setSceneRect(newSceneRect); 0244 m_infoSidebar->reset(); 0245 0246 m_infoOverlay->showGetReady(); 0247 0248 //set the minimum size for the scene 0249 m_minSize = sceneRect(); 0250 int minWidth = static_cast<int>((m_minSize.width() / Granatier::CellSize + 1.1) * Granatier::CellSize); 0251 int minHeight = static_cast<int>((m_minSize.height() / Granatier::CellSize + 1.1) * Granatier::CellSize); 0252 m_minSize.setX(m_minSize.x() + (m_minSize.width() - minWidth) / 10); 0253 m_minSize.setY(m_minSize.y() + (m_minSize.height() - minHeight) / 4); 0254 m_minSize.setWidth(minWidth); 0255 m_minSize.setHeight(minHeight); 0256 setSceneRect(m_minSize); 0257 0258 resizeSprites(); 0259 } 0260 0261 void GameScene::initItemsWithGraphicsFromTheme() 0262 { 0263 // Create the ArenaItems 0264 m_arenaItem = new ArenaItem**[m_game->getArena()->getNbRows()]; 0265 0266 for (int i = 0; i < m_game->getArena()->getNbRows(); ++i) 0267 { 0268 m_arenaItem[i] = new ArenaItem*[m_game->getArena()->getNbColumns()]; 0269 for (int j = 0; j < m_game->getArena()->getNbColumns(); ++j) 0270 { 0271 // Create the ArenaItem and set the image 0272 auto* arenaItem = new ArenaItem(j * Granatier::CellSize, i * Granatier::CellSize, m_rendererArenaItems, QStringLiteral("")); 0273 connect(this, &GameScene::resizeGraphics, arenaItem, &ArenaItem::updateGraphics); 0274 0275 //TODO: use this function call 0276 //arenaItem->setElementId(m_game->getArena()->getCell(i,j).getElement()->getImageId()); 0277 switch(m_game->getArena()->getCell(i,j).getType()) 0278 { 0279 case Granatier::Cell::WALL: 0280 arenaItem->setSpriteKey(QStringLiteral("arena_wall")); 0281 arenaItem->setZValue(-2); 0282 break; 0283 case Granatier::Cell::HOLE: 0284 delete arenaItem; 0285 arenaItem = nullptr; 0286 break; 0287 case Granatier::Cell::ICE: 0288 arenaItem->setSpriteKey(QStringLiteral("arena_ice")); 0289 arenaItem->setZValue(0); 0290 break; 0291 case Granatier::Cell::BOMBMORTAR: 0292 arenaItem->setSpriteKey(QStringLiteral("arena_bomb_mortar")); 0293 arenaItem->setZValue(0); 0294 break; 0295 case Granatier::Cell::ARROWUP: 0296 arenaItem->setSpriteKey(QStringLiteral("arena_arrow_up")); 0297 arenaItem->setZValue(0); 0298 break; 0299 case Granatier::Cell::ARROWRIGHT: 0300 arenaItem->setSpriteKey(QStringLiteral("arena_arrow_right")); 0301 arenaItem->setZValue(0); 0302 break; 0303 case Granatier::Cell::ARROWDOWN: 0304 arenaItem->setSpriteKey(QStringLiteral("arena_arrow_down")); 0305 arenaItem->setZValue(0); 0306 break; 0307 case Granatier::Cell::ARROWLEFT: 0308 arenaItem->setSpriteKey(QStringLiteral("arena_arrow_left")); 0309 arenaItem->setZValue(0); 0310 break; 0311 case Granatier::Cell::GROUND: 0312 case Granatier::Cell::BLOCK: 0313 default: 0314 arenaItem->setSpriteKey(QStringLiteral("arena_ground")); 0315 arenaItem->setZValue(-1); 0316 } 0317 m_arenaItem[i][j] = arenaItem; 0318 } 0319 } 0320 0321 // Create the Block and Bonus items 0322 m_blockItems = new BlockItem**[m_game->getArena()->getNbRows()]; 0323 m_bonusItems = new BonusItem**[m_game->getArena()->getNbRows()]; 0324 QList<Element*> blockElements; 0325 for (int i = 0; i < m_game->getArena()->getNbRows(); ++i) 0326 { 0327 m_blockItems[i] = new BlockItem*[m_game->getArena()->getNbColumns()]; 0328 m_bonusItems[i] = new BonusItem*[m_game->getArena()->getNbColumns()]; 0329 for (int j = 0; j < m_game->getArena()->getNbColumns(); ++j) 0330 { 0331 blockElements = m_game->getArena()->getCell(i, j).getElements(Granatier::Element::BLOCK); 0332 if (!blockElements.isEmpty()) 0333 { 0334 // Create the element item and set the image 0335 for(const auto& element: std::as_const(blockElements)) 0336 { 0337 auto* block = dynamic_cast <Block*> (element); 0338 auto* blockItem = new BlockItem(block, m_rendererArenaItems); 0339 connect(this, &GameScene::resizeGraphics, blockItem, &BlockItem::updateGraphics); 0340 blockItem->setSpriteKey(block->getImageId()); 0341 blockItem->update(block->getX(), block->getY()); 0342 blockItem->setZValue(200); 0343 if(Settings::self()->showAllTiles() == 1) 0344 { 0345 blockItem->setZValue(99); 0346 } 0347 connect(this, &GameScene::resizeGraphics, blockItem, &BlockItem::updateGraphics); 0348 connect(blockItem, &BlockItem::blockItemDestroyed, this, &GameScene::removeBlockItem); 0349 m_blockItems[i][j] = blockItem; 0350 // if the block contains a hidden bonus, create the bonus item 0351 Bonus* bonus = block->getBonus(); 0352 if(bonus) 0353 { 0354 auto* bonusItem = new BonusItem(bonus, m_rendererBonusItems); 0355 switch(bonus->getBonusType()) 0356 { 0357 case Granatier::Bonus::SPEED: 0358 bonusItem->setSpriteKey(QStringLiteral("bonus_speed")); 0359 break; 0360 case Granatier::Bonus::BOMB: 0361 bonusItem->setSpriteKey(QStringLiteral("bonus_bomb")); 0362 break; 0363 case Granatier::Bonus::POWER: 0364 bonusItem->setSpriteKey(QStringLiteral("bonus_power")); 0365 break; 0366 case Granatier::Bonus::SHIELD: 0367 bonusItem->setSpriteKey(QStringLiteral("bonus_shield")); 0368 break; 0369 case Granatier::Bonus::THROW: 0370 bonusItem->setSpriteKey(QStringLiteral("bonus_throw")); 0371 break; 0372 case Granatier::Bonus::KICK: 0373 bonusItem->setSpriteKey(QStringLiteral("bonus_kick")); 0374 break; 0375 case Granatier::Bonus::HYPERACTIVE: 0376 bonusItem->setSpriteKey(QStringLiteral("bonus_bad_hyperactive")); 0377 break; 0378 case Granatier::Bonus::SLOW: 0379 bonusItem->setSpriteKey(QStringLiteral("bonus_bad_slow")); 0380 break; 0381 case Granatier::Bonus::MIRROR: 0382 bonusItem->setSpriteKey(QStringLiteral("bonus_bad_mirror")); 0383 break; 0384 case Granatier::Bonus::SCATTY: 0385 bonusItem->setSpriteKey(QStringLiteral("bonus_bad_scatty")); 0386 break; 0387 case Granatier::Bonus::RESTRAIN: 0388 bonusItem->setSpriteKey(QStringLiteral("bonus_bad_restrain")); 0389 break; 0390 case Granatier::Bonus::RESURRECT: 0391 bonusItem->setSpriteKey(QStringLiteral("bonus_neutral_resurrect")); 0392 break; 0393 default: 0394 bonusItem->setSpriteKey(QStringLiteral("bonus_neutral_pandora")); 0395 } 0396 0397 if(granatier::RNG::fromRange(0, 10) > 9 && bonusItem->spriteKey() != QStringLiteral("bonus_neutral_resurrect")) 0398 { 0399 bonusItem->setSpriteKey(QStringLiteral("bonus_neutral_pandora")); 0400 } 0401 0402 bonusItem->update(bonus->getX(), bonus->getY()); 0403 bonusItem->setZValue(100); 0404 m_bonusItems[i][j] = bonusItem; 0405 0406 connect(this, &GameScene::resizeGraphics, bonusItem, &BonusItem::updateGraphics); 0407 connect(bonusItem, &BonusItem::bonusItemDestroyed, this, &GameScene::removeBonusItem); 0408 0409 addItem(bonusItem); 0410 if(Settings::self()->showAllTiles() == 0) 0411 { 0412 bonusItem->hide(); 0413 } 0414 } 0415 else 0416 { 0417 m_bonusItems[i][j] = nullptr; 0418 } 0419 } 0420 } 0421 else 0422 { 0423 m_blockItems[i][j] = nullptr; 0424 m_bonusItems[i][j] = nullptr; 0425 } 0426 } 0427 } 0428 0429 // Create the Bomb items 0430 for(auto& bomb: m_tempBombList) 0431 { 0432 createBombItem(bomb); 0433 } 0434 if(!m_tempBombList.isEmpty()) 0435 { 0436 QHash<BombItem*, QList<BombExplosionItem*> >::iterator i = m_bombItems.begin(); 0437 while (i != m_bombItems.end()) 0438 { 0439 i.key()->pauseAnim(); 0440 i++; 0441 } 0442 } 0443 0444 // Display the ArenaItem 0445 for (int i = 0; i < m_game->getArena()->getNbRows();++i) 0446 { 0447 for (int j = 0; j < m_game->getArena()->getNbColumns(); ++j) 0448 { 0449 if(m_arenaItem[i][j] != nullptr) 0450 { 0451 addItem(m_arenaItem[i][j]); 0452 } 0453 } 0454 } 0455 0456 // Display the Block Items 0457 for (int i = 0; i < m_game->getArena()->getNbRows(); ++i) 0458 { 0459 for (int j = 0; j < m_game->getArena()->getNbColumns(); ++j) 0460 { 0461 if (m_blockItems[i][j] != nullptr) 0462 { 0463 if (!items().contains(m_blockItems[i][j])) 0464 { 0465 addItem(m_blockItems[i][j]); 0466 } 0467 } 0468 } 0469 } 0470 0471 //create the background 0472 m_arenaBackground = new KGameRenderedItem(m_rendererBackground, QStringLiteral("background")); 0473 m_arenaBackground->setZValue(-5); 0474 m_arenaBackground->setPos(0, 0); 0475 m_arenaBackground->setCacheMode(QGraphicsItem::NoCache); // if cache is set, there are some artifacts; pay attention, that the KGameRenderer cache is used nevertheless 0476 m_arenaBackground->setRenderSize(QSize(100, 100)); //just to get something in the background, until the right size is rendered 0477 addItem(m_arenaBackground); 0478 0479 resizeSprites(); 0480 } 0481 0482 GameScene::~GameScene() 0483 { 0484 delete m_backgroundResizeTimer; 0485 0486 cleanUp(); 0487 0488 delete m_infoOverlay; 0489 0490 for (auto & playerItem : m_playerItems) 0491 { 0492 if(items().contains(playerItem)) 0493 { 0494 removeItem(playerItem); 0495 } 0496 playerItem->stopAnim(); 0497 delete playerItem; 0498 } 0499 0500 QMap <Player*, KGameRenderer*>::iterator iteratorRendererPlayer = m_mapRendererPlayerItems.begin(); 0501 while (iteratorRendererPlayer != m_mapRendererPlayerItems.end()) 0502 { 0503 delete iteratorRendererPlayer.value(); 0504 iteratorRendererPlayer++; 0505 } 0506 0507 delete m_rendererSelectedTheme; 0508 delete m_rendererDefaultTheme; 0509 } 0510 0511 void GameScene::cleanUp() 0512 { 0513 cleanUpItemsWithGraphicsFromTheme(); 0514 0515 delete m_infoSidebar; 0516 0517 if(items().contains(m_remainingTimeLabel)) 0518 { 0519 removeItem(m_remainingTimeLabel); 0520 } 0521 0522 if(items().contains(m_arenaNameLabel)) 0523 { 0524 removeItem(m_arenaNameLabel); 0525 } 0526 } 0527 0528 void GameScene::cleanUpItemsWithGraphicsFromTheme() 0529 { 0530 // remove the arena items 0531 for (int i = 0; i < m_game->getArena()->getNbRows();++i) 0532 { 0533 for (int j = 0; j < m_game->getArena()->getNbColumns(); ++j) 0534 { 0535 if (m_arenaItem[i][j] != nullptr) 0536 { 0537 if (items().contains(m_arenaItem[i][j])) 0538 { 0539 removeItem(m_arenaItem[i][j]); 0540 } 0541 delete m_arenaItem[i][j]; 0542 } 0543 } 0544 delete[] m_arenaItem[i]; 0545 } 0546 delete[] m_arenaItem; 0547 0548 // Find the BombItem and remove it 0549 BombExplosionItem* bombExplosionItem; 0550 QHash<BombItem*, QList<BombExplosionItem*> >::iterator i = m_bombItems.begin(); 0551 while (i != m_bombItems.end()) 0552 { 0553 while(!i.value().isEmpty()) 0554 { 0555 bombExplosionItem = i.value().takeFirst(); 0556 if(items().contains(bombExplosionItem)) 0557 { 0558 removeItem(bombExplosionItem); 0559 } 0560 delete bombExplosionItem; 0561 } 0562 if(items().contains(i.key())) 0563 { 0564 removeItem(i.key()); 0565 } 0566 if(dynamic_cast <Bomb*> (i.key()->getModel())->isDetonated()) 0567 { 0568 dynamic_cast <Bomb*> (i.key()->getModel())->slot_detonationCompleted(); 0569 } 0570 delete i.key(); 0571 i = m_bombItems.erase(i); 0572 } 0573 0574 // Find the BlockItems and BonusItems and remove it 0575 for (int i = 0; i < m_game->getArena()->getNbRows();++i) 0576 { 0577 for (int j = 0; j < m_game->getArena()->getNbColumns(); ++j) 0578 { 0579 if (m_blockItems[i][j] != nullptr) 0580 { 0581 if (items().contains(m_blockItems[i][j])) 0582 { 0583 removeItem(m_blockItems[i][j]); 0584 } 0585 delete m_blockItems[i][j]; 0586 } 0587 if (m_bonusItems[i][j] != nullptr) 0588 { 0589 if (items().contains(m_bonusItems[i][j])) 0590 { 0591 removeItem(m_bonusItems[i][j]); 0592 } 0593 delete m_bonusItems[i][j]; 0594 } 0595 } 0596 delete[] m_blockItems[i]; 0597 delete[] m_bonusItems[i]; 0598 } 0599 delete[] m_blockItems; 0600 delete[] m_bonusItems; 0601 0602 removeItem(m_arenaBackground); 0603 delete m_arenaBackground; 0604 } 0605 0606 void GameScene::themeChanged() 0607 { 0608 m_tempBombList.clear(); 0609 QHash<BombItem*, QList<BombExplosionItem*> >::iterator i = m_bombItems.begin(); 0610 while (i != m_bombItems.end()) 0611 { 0612 if(!(dynamic_cast <Bomb*> (i.key()->getModel())->isDetonated())) 0613 { 0614 m_tempBombList.append(dynamic_cast <Bomb*> (i.key()->getModel())); 0615 } 0616 i++; 0617 } 0618 0619 cleanUpItemsWithGraphicsFromTheme(); 0620 setupThemeRenderer(); 0621 initItemsWithGraphicsFromTheme(); 0622 m_infoSidebar->themeChanged(); 0623 m_infoOverlay->themeChanged(); 0624 0625 m_tempBombList.clear(); 0626 } 0627 0628 KGameRenderer* GameScene::renderer(Granatier::Element::Type type, Player* player) 0629 { 0630 switch(type) 0631 { 0632 case Granatier::Element::BLOCK: 0633 return m_rendererArenaItems; 0634 case Granatier::Element::BONUS: 0635 return m_rendererBonusItems; 0636 case Granatier::Element::BOMB: 0637 return m_rendererBombItems; 0638 case Granatier::Element::PLAYER: 0639 return m_mapRendererPlayerItems.value(player); 0640 case Granatier::Element::SCORE: 0641 return m_rendererScoreItems; 0642 default: 0643 return nullptr; 0644 } 0645 } 0646 0647 void GameScene::showScore() 0648 { 0649 m_infoOverlay->showScore(); 0650 } 0651 0652 void GameScene::resizeSprites(int delayForBackground) 0653 { 0654 if(views().isEmpty()) 0655 { 0656 return; 0657 } 0658 0659 //calculate the scaling factor for the SVGs 0660 int horizontalPixelsPerCell = static_cast<int>(views().constFirst()->size().width() / (m_minSize.width()/Granatier::CellSize)); 0661 int verticalPixelsPerCell = static_cast<int>(views().constFirst()->size().height() / (m_minSize.height()/Granatier::CellSize)); 0662 if(horizontalPixelsPerCell < verticalPixelsPerCell) 0663 { 0664 m_SvgScaleFactor = Granatier::CellSize / horizontalPixelsPerCell; 0665 } 0666 else 0667 { 0668 m_SvgScaleFactor = Granatier::CellSize / verticalPixelsPerCell; 0669 } 0670 QTransform transform; 0671 transform.scale(1/m_SvgScaleFactor, 1/m_SvgScaleFactor); 0672 views().constFirst()->setTransform(transform); 0673 views().constFirst()->centerOn(sceneRect().center()); 0674 views().constFirst()->updateSceneRect(m_minSize);; 0675 0676 //update pixmaps 0677 Q_EMIT resizeGraphics(m_SvgScaleFactor); 0678 0679 //update overlay 0680 QRect viewRect = views().constFirst()->rect(); 0681 QRectF viewRectToScene = QRectF(views().constFirst()->mapToScene(viewRect.topLeft()), views().constFirst()->mapToScene(viewRect.bottomRight())); 0682 m_infoOverlay->resizeDimmOverlay(viewRectToScene.x(), viewRectToScene.y(), viewRectToScene.width(), viewRectToScene.height()); 0683 0684 //update background pixmap 0685 m_arenaBackground->setPos(views().constFirst()->mapToScene(viewRect.topLeft())); 0686 m_arenaBackground->setScale(m_SvgScaleFactor); 0687 0688 m_arenaBackground->setPixmap(m_arenaBackground->pixmap().scaled(viewRect.size())); 0689 0690 m_backgroundResizeTimer->stop(); 0691 m_backgroundResizeTimer->start(delayForBackground); 0692 } 0693 0694 void GameScene::resizeBackground() 0695 { 0696 if(views().isEmpty()) 0697 { 0698 return; 0699 } 0700 QRect viewRect = views().constFirst()->rect(); 0701 m_arenaBackground->setRenderSize(viewRect.size()); 0702 } 0703 0704 Game* GameScene::getGame() const 0705 { 0706 return m_game; 0707 } 0708 0709 void GameScene::start() 0710 { 0711 // hide the info items 0712 m_infoOverlay->hideItems(); 0713 } 0714 0715 void GameScene::setPaused(const bool p_pause, const bool p_fromUser) 0716 { 0717 // If the game has paused 0718 if (p_pause) 0719 { 0720 // If the pause is due to an action from the user 0721 if (p_fromUser) 0722 { 0723 m_infoOverlay->showPause(); 0724 } 0725 0726 // Stop player animation 0727 for (auto & playerItem : m_playerItems) 0728 { 0729 playerItem->pauseAnim(); 0730 } 0731 // Stop bomb animation 0732 QHash<BombItem*, QList<BombExplosionItem*> >::iterator i = m_bombItems.begin(); 0733 while (i != m_bombItems.end()) 0734 { 0735 i.key()->pauseAnim(); 0736 ++i; 0737 } 0738 } 0739 else 0740 { // If the game has resumed, hide the info items 0741 m_infoOverlay->hideItems(); 0742 0743 // Resume player animation 0744 for (auto & playerItem : m_playerItems) 0745 { 0746 playerItem->resumeAnim(); 0747 } 0748 // Resume bomb animation 0749 QHash<BombItem*, QList<BombExplosionItem*> >::iterator i = m_bombItems.begin(); 0750 while (i != m_bombItems.end()) 0751 { 0752 i.key()->resumeAnim(); 0753 ++i; 0754 } 0755 } 0756 } 0757 0758 void GameScene::removeBlockItem(BlockItem* blockItem) 0759 { 0760 // remove the Bonus Items 0761 for (int i = 0; i < m_game->getArena()->getNbRows(); ++i) 0762 { 0763 for (int j = 0; j < m_game->getArena()->getNbColumns(); ++j) 0764 { 0765 if (m_blockItems[i][j] != nullptr && m_blockItems[i][j] == blockItem) 0766 { 0767 if (items().contains(m_blockItems[i][j])) 0768 { 0769 removeItem(m_blockItems[i][j]); 0770 m_blockItems[i][j] = nullptr; 0771 m_game->blockDestroyed(i, j, dynamic_cast <Block*> (blockItem->getModel())); 0772 delete blockItem; 0773 } 0774 } 0775 } 0776 } 0777 } 0778 0779 void GameScene::removeBonusItem(BonusItem* bonusItem) 0780 { 0781 // remove the Bonus Items 0782 for (int i = 0; i < m_game->getArena()->getNbRows(); ++i) 0783 { 0784 for (int j = 0; j < m_game->getArena()->getNbColumns(); ++j) 0785 { 0786 if (m_bonusItems[i][j] != nullptr && m_bonusItems[i][j] == bonusItem) 0787 { 0788 if (items().contains(m_bonusItems[i][j])) 0789 { 0790 removeItem(m_bonusItems[i][j]); 0791 m_bonusItems[i][j] = nullptr; 0792 m_game->removeBonus(dynamic_cast <Bonus*> (bonusItem->getModel())); 0793 delete bonusItem; 0794 } 0795 } 0796 } 0797 } 0798 } 0799 0800 void GameScene::updateInfo(const Granatier::Info::Type p_info) 0801 { 0802 if(p_info == Granatier::Info::TimeInfo) 0803 { 0804 int nTime = m_game->getRemainingTime(); 0805 if(nTime > 0) 0806 { 0807 m_remainingTimeLabel->setPlainText(QStringLiteral("%1:%2").arg(nTime/60).arg(nTime%60, 2, 10, QLatin1Char('0'))); 0808 } 0809 else 0810 { 0811 m_remainingTimeLabel->setPlainText(i18n("Sudden Death")); 0812 m_remainingTimeLabel->setDefaultTextColor(QColor("#FF0000")); 0813 m_remainingTimeLabel->setPos(Granatier::CellSize * m_game->getArena()->getNbColumns() - m_remainingTimeLabel->boundingRect().width(), - m_remainingTimeLabel->boundingRect().height()); 0814 } 0815 } 0816 } 0817 0818 void GameScene::createBombItem(Bomb* bomb) 0819 { 0820 // Create the Bombs 0821 auto* bombItem = new BombItem(bomb, m_rendererBombItems); 0822 // Corrects the position of the BombItem 0823 bombItem->update(bomb->getX(), bomb->getY()); 0824 addItem(bombItem); 0825 m_bombItems[bombItem].append(nullptr); 0826 0827 bombItem->updateGraphics(m_SvgScaleFactor); //TODO: use a Renderer class and get the scale factor from a static function during initialization 0828 0829 connect(this, &GameScene::resizeGraphics, bombItem, &BombItem::updateGraphics); 0830 connect(bomb, &Bomb::mortar, bombItem, &BombItem::updateMortar); 0831 connect(bomb, &Bomb::bombDetonated, this, &GameScene::bombDetonated); 0832 connect(bombItem, &BombItem::bombItemFinished, this, &GameScene::removeBombItem); 0833 connect(bombItem, &BombItem::animationFrameChanged, this, &GameScene::updateBombExplosionItemAnimation); 0834 } 0835 0836 void GameScene::removeBombItem(BombItem* bombItem) 0837 { 0838 m_game->removeBomb(dynamic_cast <Bomb*> (bombItem->getModel())); 0839 // Find the BombItem and remove it 0840 BombExplosionItem* bombExplosionItem; 0841 QHash<BombItem*, QList<BombExplosionItem*> >::iterator i = m_bombItems.begin(); 0842 while (i != m_bombItems.end()) 0843 { 0844 if(i.key() == bombItem) 0845 { 0846 while(!i.value().isEmpty()) 0847 { 0848 bombExplosionItem = i.value().takeFirst(); 0849 if(items().contains(bombExplosionItem)) 0850 { 0851 removeItem(bombExplosionItem); 0852 } 0853 delete bombExplosionItem; 0854 } 0855 if(items().contains(i.key())) 0856 { 0857 removeItem(i.key()); 0858 } 0859 delete i.key(); 0860 i = m_bombItems.erase(i); 0861 break; 0862 } 0863 else 0864 { 0865 ++i; 0866 } 0867 } 0868 } 0869 0870 void GameScene::bombDetonated(Bomb* bomb) 0871 { 0872 BombItem* bombItem = nullptr; 0873 BombExplosionItem* bombExplosionItem = nullptr; 0874 QList<Element*> blockElements; 0875 QList<Element*> bombElements; 0876 int nBombPower = bomb->bombPower(); 0877 int nNumberOfColums = m_game->getArena()->getNbColumns(); 0878 int nNumberOfRows = m_game->getArena()->getNbRows(); 0879 int nColumn; 0880 int nRow; 0881 qreal xPos = 0; 0882 qreal yPos = 0; 0883 bool abDirectionsDone[] = {false, false, false, false}; 0884 int nDetonationCountdown = 75; 0885 int anDirectionDetonationCountdown[] = {nDetonationCountdown, nDetonationCountdown, nDetonationCountdown, nDetonationCountdown}; 0886 0887 // Find the BombItem from the Bomb 0888 QHash<BombItem*, QList<BombExplosionItem*> >::iterator i = m_bombItems.begin(); 0889 while (i != m_bombItems.end()) 0890 { 0891 if(i.key()->getModel() == bomb) 0892 { 0893 bombItem = i.key(); 0894 break; 0895 } 0896 ++i; 0897 } 0898 0899 if(!bombItem) 0900 { 0901 return; 0902 } 0903 0904 //check if there is a bomb or block at the position where the bomb exploded (possible when thrown) 0905 nColumn = m_game->getArena()->getColFromX(bomb->getX()); 0906 nRow = m_game->getArena()->getRowFromY(bomb->getY()); 0907 if(nColumn >= 0 && nColumn < nNumberOfColums && nRow >= 0 && nRow < nNumberOfRows) 0908 { 0909 bombElements = m_game->getArena()->getCell(nRow, nColumn).getElements(Granatier::Element::BOMB); 0910 int tempBombDetonationCountdown = nDetonationCountdown; 0911 for(const auto& element: std::as_const(bombElements)) 0912 { 0913 if(dynamic_cast <Bomb*> (element) != bomb && !(dynamic_cast <Bomb*> (element)->isDetonated())) 0914 { 0915 dynamic_cast <Bomb*> (element)->initDetonation(bomb->explosionID(), tempBombDetonationCountdown); 0916 tempBombDetonationCountdown += 10; 0917 } 0918 } 0919 0920 blockElements = m_game->getArena()->getCell(nRow, nColumn).getElements(Granatier::Element::BLOCK); 0921 if(!blockElements.isEmpty()) 0922 { 0923 for(const auto& element: std::as_const(blockElements)) 0924 { 0925 dynamic_cast <Block*> (element)->startDestruction(); 0926 if (m_blockItems[nRow][nColumn] != nullptr) 0927 { 0928 //display bonus if available 0929 if (m_bonusItems[nRow][nColumn] != nullptr) 0930 { 0931 m_bonusItems[nRow][nColumn]->setUndestroyable(bomb->explosionID()); 0932 m_bonusItems[nRow][nColumn]->show(); 0933 } 0934 } 0935 } 0936 } 0937 else if(m_bonusItems[nRow][nColumn] != nullptr) 0938 { 0939 m_bonusItems[nRow][nColumn]->initDestruction(bomb->explosionID()); 0940 } 0941 } 0942 0943 for(int i = 0; i < nBombPower; i++) 0944 { 0945 Granatier::Direction::Type direction = Granatier::Direction::NORTH; 0946 do 0947 { 0948 switch(direction) 0949 { 0950 case Granatier::Direction::NORTH: 0951 xPos = bomb->getX(); 0952 yPos = bomb->getY() - (i+1)*Granatier::CellSize; 0953 break; 0954 case Granatier::Direction::EAST: 0955 xPos = bomb->getX() + (i+1)*Granatier::CellSize; 0956 yPos = bomb->getY(); 0957 break; 0958 case Granatier::Direction::SOUTH: 0959 xPos = bomb->getX(); 0960 yPos = bomb->getY() + (i+1)*Granatier::CellSize; 0961 break; 0962 case Granatier::Direction::WEST: 0963 xPos = bomb->getX() - (i+1)*Granatier::CellSize; 0964 yPos = bomb->getY(); 0965 break; 0966 } 0967 nColumn = m_game->getArena()->getColFromX(xPos); 0968 nRow = m_game->getArena()->getRowFromY(yPos); 0969 0970 if(!abDirectionsDone[direction] && nColumn >= 0 && nColumn < nNumberOfColums && nRow >= 0 && nRow < nNumberOfRows) 0971 { 0972 bombElements = m_game->getArena()->getCell(nRow, nColumn).getElements(Granatier::Element::BOMB); 0973 blockElements = m_game->getArena()->getCell(nRow, nColumn).getElements(Granatier::Element::BLOCK); 0974 if(m_game->getArena()->getCell(nRow, nColumn).getType() != Granatier::Cell::WALL) 0975 { 0976 int tempBombDetonationCountdown = anDirectionDetonationCountdown[direction]; 0977 bool increaseDetonationTimeout = false; 0978 for(const auto& element: std::as_const(bombElements)) 0979 { 0980 if(dynamic_cast <Bomb*> (element) != bomb && !(dynamic_cast <Bomb*> (element)->isDetonated())) 0981 { 0982 dynamic_cast <Bomb*> (element)->initDetonation(bomb->explosionID(), tempBombDetonationCountdown); 0983 tempBombDetonationCountdown += 10; 0984 increaseDetonationTimeout = true; 0985 } 0986 } 0987 if(increaseDetonationTimeout) 0988 { 0989 anDirectionDetonationCountdown[direction] += nDetonationCountdown; 0990 } 0991 0992 if(!blockElements.isEmpty()) 0993 { 0994 abDirectionsDone[direction] = true; 0995 for(const auto& element: std::as_const(blockElements)) 0996 { 0997 dynamic_cast <Block*> (element)->startDestruction(); 0998 if (m_blockItems[nRow][nColumn] != nullptr) 0999 { 1000 //display bonus if available 1001 if (m_bonusItems[nRow][nColumn] != nullptr) 1002 { 1003 m_bonusItems[nRow][nColumn]->setUndestroyable(bomb->explosionID()); 1004 m_bonusItems[nRow][nColumn]->show(); 1005 } 1006 } 1007 } 1008 } 1009 else if(m_bonusItems[nRow][nColumn] != nullptr) 1010 { 1011 m_bonusItems[nRow][nColumn]->initDestruction(bomb->explosionID()); 1012 } 1013 1014 bombExplosionItem = new BombExplosionItem (bomb, direction, nBombPower - i, m_rendererBombItems, m_SvgScaleFactor); 1015 bombExplosionItem->setPosition(xPos, yPos); 1016 connect(this, &GameScene::resizeGraphics, bombExplosionItem, &BombExplosionItem::updateGraphics); 1017 addItem(bombExplosionItem); 1018 m_bombItems[bombItem].append(bombExplosionItem); 1019 } 1020 else 1021 { 1022 abDirectionsDone[direction] = true; 1023 } 1024 } 1025 else 1026 { 1027 abDirectionsDone[direction] = true; 1028 } 1029 1030 switch(direction) 1031 { 1032 case Granatier::Direction::NORTH: 1033 direction = Granatier::Direction::EAST; 1034 break; 1035 case Granatier::Direction::EAST: 1036 direction = Granatier::Direction::SOUTH; 1037 break; 1038 case Granatier::Direction::SOUTH: 1039 direction = Granatier::Direction::WEST; 1040 break; 1041 case Granatier::Direction::WEST: 1042 direction = Granatier::Direction::NORTH; 1043 break; 1044 } 1045 } 1046 while(direction != Granatier::Direction::NORTH); 1047 } 1048 } 1049 1050 void GameScene::updateBombExplosionItemAnimation(BombItem* bombItem, int nFrame) 1051 { 1052 // Find the BombItems and update the frame 1053 BombExplosionItem* bombExplosionItem; 1054 QHash<BombItem*, QList<BombExplosionItem*> >::iterator i = m_bombItems.begin(); 1055 while (i != m_bombItems.end()) 1056 { 1057 if(i.key() == bombItem) 1058 { 1059 for(int nIndex = 0; nIndex < i.value().count(); nIndex++) 1060 { 1061 bombExplosionItem = i.value().at(nIndex); 1062 if(bombExplosionItem) 1063 { 1064 bombExplosionItem->updateAnimationn(nFrame); 1065 } 1066 } 1067 break; 1068 } 1069 else 1070 { 1071 ++i; 1072 } 1073 } 1074 } 1075 1076 #include "moc_gamescene.cpp"