File indexing completed on 2024-04-14 03:59:51

0001 /*
0002     SPDX-FileCopyrightText: 2012 Christian Krippendorf <Coding@Christian-Krippendorf.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // own
0008 #include "gamescene.h"
0009 
0010 // Qt
0011 #include <QGraphicsSceneMouseEvent>
0012 #include <QList>
0013 
0014 // KMahjongg
0015 #include "gamedata.h"
0016 #include "gameitem.h"
0017 #include "gameview.h"
0018 #include "kmahjongglayout.h"
0019 #include "gamebackground.h"
0020 #include "gameremovedtiles.h"
0021 
0022 
0023 GameScene::GameScene(QObject * parent)
0024     : QGraphicsScene(parent)
0025     , m_pFirstSelectedItem(nullptr)
0026     , m_pSecondSelectedItem(nullptr)
0027     , m_gameBackground(nullptr)
0028     , m_gameRemovedTiles(nullptr)
0029 {
0030     initializeGameItemsArray();
0031 }
0032 
0033 GameScene::~GameScene()
0034 {
0035 }
0036 
0037 void GameScene::clearGameItems()
0038 {
0039     // Remove all GameItem objects and DON'T remove GameBackground object.
0040     QList<QGraphicsItem*> items = QGraphicsScene::items();
0041     for (int i = 0; i < items.size(); i++) {
0042         GameItem *gameItem = dynamic_cast<GameItem*>(items.at(i));
0043         if (gameItem != nullptr) {
0044             QGraphicsScene::removeItem(gameItem);
0045         }
0046     }
0047     initializeGameItemsArray();
0048 
0049     m_pFirstSelectedItem = nullptr;
0050     m_pSecondSelectedItem = nullptr;
0051 }
0052 
0053 void GameScene::clear()
0054 {
0055     QGraphicsScene::clear();
0056     initializeGameItemsArray();
0057 
0058     m_pFirstSelectedItem = nullptr;
0059     m_pSecondSelectedItem = nullptr;
0060 }
0061 
0062 void GameScene::initializeGameItemsArray()
0063 {
0064     // initialize all array pointers with nullptr.
0065     for (int i = 0; i < BOARD_WIDTH; ++i) {
0066         for (int j = 0; j < BOARD_HEIGHT; ++j) {
0067             for (int k = 0; k < BOARD_DEPTH; ++k) {
0068                 m_pGameItemsArray[i][j][k] = nullptr;
0069             }
0070         }
0071     }
0072 }
0073 
0074 void GameScene::setRemovedTilesItem(GameRemovedTiles * gameRemovedTiles)
0075 {
0076     // If a removedtiles object already exist, delete it from scene
0077     if (m_gameRemovedTiles != nullptr) {
0078         QGraphicsScene::removeItem(m_gameRemovedTiles);
0079     }
0080 
0081     m_gameRemovedTiles = gameRemovedTiles;
0082     QGraphicsScene::addItem(gameRemovedTiles);
0083 }
0084 
0085 void GameScene::setBackgroundItem(GameBackground * gameBackground)
0086 {
0087     // If a background exist, delete it from scene
0088     if (m_gameBackground != nullptr) {
0089         QGraphicsScene::removeItem(m_gameBackground);
0090     }
0091 
0092     m_gameBackground = gameBackground;
0093     QGraphicsScene::addItem(gameBackground);
0094 }
0095 
0096 void GameScene::addItem(GameItem * gameItem)
0097 {
0098     QGraphicsScene::addItem(gameItem);
0099 
0100     // Add the item to the position array.
0101     addItemToPositionArray(gameItem);
0102 }
0103 
0104 void GameScene::removeItem(GameItem * gameItem)
0105 {
0106     USHORT x = gameItem->getGridPosX();
0107     USHORT y = gameItem->getGridPosY();
0108     USHORT z = gameItem->getGridPosZ();
0109     m_pGameItemsArray[x][y][z] = nullptr;
0110 
0111     QGraphicsScene::removeItem(gameItem);
0112 }
0113 
0114 void GameScene::removeItem(POSITION const & stItemPos)
0115 {
0116     GameItem * gameItem = m_pGameItemsArray[stItemPos.x][stItemPos.y][stItemPos.z];
0117 
0118     if (gameItem != nullptr) {
0119         removeItem(gameItem);
0120     }
0121 }
0122 
0123 void GameScene::addItemToPositionArray(GameItem * const gameItem)
0124 {
0125     // Take a look, if the place is already taken.
0126     USHORT x = gameItem->getGridPosX();
0127     USHORT y = gameItem->getGridPosY();
0128     USHORT z = gameItem->getGridPosZ();
0129     if (m_pGameItemsArray[x][y][z] == nullptr) {
0130         m_pGameItemsArray[x][y][z] = gameItem;
0131     }
0132 }
0133 
0134 GameItem * GameScene::getItemOnGridPos(int x, int y, int z)
0135 {
0136     // Test for range
0137     if ((x < 0 || x > BOARD_WIDTH - 1) || (y < 0 || y > BOARD_HEIGHT - 1) || (z < 0 || z > BOARD_DEPTH - 1)) {
0138         return nullptr;
0139     }
0140 
0141     return m_pGameItemsArray[x][y][z];
0142 }
0143 
0144 GameItem * GameScene::getItemOnGridPos(POSITION & stItemPos)
0145 {
0146     return getItemOnGridPos(stItemPos.x, stItemPos.y, stItemPos.z);
0147 }
0148 
0149 bool GameScene::isItemOnGridPos(int x, int y, int z) const
0150 {
0151     // Test for range
0152     if ((x < 0 || x > BOARD_WIDTH - 1) || (y < 0 || y > BOARD_HEIGHT - 1) || (z < 0 || z > BOARD_DEPTH - 1)) {
0153         return false;
0154     }
0155 
0156     return !(m_pGameItemsArray[x][y][z] == nullptr);
0157 }
0158 
0159 QList<GameItem *> GameScene::selectedItems() const
0160 {
0161     QList<QGraphicsItem *> originalList = QGraphicsScene::selectedItems();
0162     QList<GameItem *> tmpList;
0163 
0164     tmpList.reserve(originalList.size());
0165     for (int i = 0; i < originalList.size(); ++i) {
0166         tmpList.append(dynamic_cast<GameItem *>(originalList.at(i)));
0167     }
0168 
0169     return tmpList;
0170 }
0171 
0172 QList<GameItem *> GameScene::items() const
0173 {
0174     QList<QGraphicsItem *> originalList = QGraphicsScene::items();
0175     QList<GameItem *> tmpList;
0176 
0177     tmpList.reserve(originalList.size());
0178     for (int i = 0; i < originalList.size(); ++i) {
0179         GameItem * gameItem = dynamic_cast<GameItem *>(originalList.at(i));
0180         if (gameItem != nullptr) {
0181             tmpList.append(gameItem);
0182         }
0183     }
0184 
0185     return tmpList;
0186 }
0187 
0188 bool GameScene::isSelectable(const GameItem * const gameItem) const
0189 {
0190     USHORT x = gameItem->getGridPosX();
0191     USHORT y = gameItem->getGridPosY();
0192     USHORT z = gameItem->getGridPosZ();
0193 
0194     // Test for items above...
0195 
0196     // We need one layer above.
0197     ++z;
0198 
0199     for (int i = x - 1; i <= x + 1; ++i) {
0200         for (int j = y - 1; j <= y + 1; ++j) {
0201             // If there is a stone on the position, the item is not selectable.
0202             if (isItemOnGridPos(i, j, z)) {
0203                 return false;
0204             }
0205         }
0206     }
0207 
0208     // Test for items beside...
0209 
0210     // Go back to the layer of the item.
0211     --z;
0212 
0213     bool sideFree = true;
0214     for (int i = x - 2; i <= x + 2; i += 4) {
0215         for (int j = y - 1; j <= y + 1; ++j) {
0216             // If there is one item on the side, it is no longer free.
0217             if (isItemOnGridPos(i, j, z)) {
0218                 sideFree = false;
0219             }
0220         }
0221 
0222         // If a side is free the item is selectable.
0223         if (sideFree == true) {
0224             return true;
0225         } else {
0226             sideFree = true;
0227         }
0228     }
0229 
0230     return false;
0231 }
0232 
0233 void GameScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent)
0234 {
0235     // Swallow this event.  A double-click causes a mousePressEvent() AND a
0236     // mouseDoubleClickEvent().  The second event could cause a tile that is
0237     // NOT removable to become selected (by default), allowing it to be removed
0238     // illegally (ie. it has a tile on each side or is half under another tile).
0239     mouseEvent->accept(); // Double-click ==> single-click in KMahjongg.
0240 }
0241 
0242 void GameScene::mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)
0243 {
0244     // N.B. Event occurs when there is a click OR double-click with ANY button.
0245     GameItem * gameItem = dynamic_cast<GameItem *>(
0246         itemAt(mouseEvent->scenePos().x(),
0247         mouseEvent->scenePos().y(), QTransform())
0248     );
0249 
0250     // An item was clicked.
0251     if (gameItem != nullptr) {
0252         // If we click on a shadow of the actual item, we have to correct the 
0253         // clicking position, in order to simulate a transparent shadow.
0254         if (gameItem->isShadow(mouseEvent->scenePos() - gameItem->pos())) {
0255             gameItem = dynamic_cast<GameItem *>(
0256                 itemAt(mouseEvent->scenePos().x() + gameItem->getShadowDeltaX(),
0257                 mouseEvent->scenePos().y() + gameItem->getShadowDeltaY(), 
0258                 QTransform())
0259             );
0260         }
0261     }
0262 
0263     // No item was clicked.
0264     if (gameItem == nullptr) {
0265         Q_EMIT clearSelectedTile();
0266         mouseEvent->ignore();
0267         return;
0268     }
0269 
0270     // If the item is selectable go on with selection.
0271     if (isSelectable(gameItem)) {
0272         clearSelection();
0273         gameItem->setSelected(true);
0274         mouseEvent->accept();
0275     } else {
0276         Q_EMIT clearSelectedTile();
0277     }
0278 }
0279 
0280 void GameScene::wheelEvent(QGraphicsSceneWheelEvent * mouseEvent)
0281 {
0282     if (mouseEvent->delta() < 0) {
0283         Q_EMIT rotateCW();
0284     } else {
0285         Q_EMIT rotateCCW();
0286     }
0287 }
0288 
0289 #include "moc_gamescene.cpp"