diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /build*/ *.kdev4 CMakeLists.txt.user* +.vscode diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,8 @@ editor.cpp frameimage.cpp gameitem.cpp + gamebackground.cpp + gameremovedtiles.cpp gameview.cpp gamescene.cpp selectionanimation.cpp diff --git a/src/gamebackground.h b/src/gamebackground.h new file mode 100644 --- /dev/null +++ b/src/gamebackground.h @@ -0,0 +1,87 @@ +/* Copyright (C) 2019 Christian Krippendorf + * + * Kmahjongg is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef GAMEBACKGROUND_H +#define GAMEBACKGROUND_H + +// Qt +#include +#include +#include + +// KMahjongg +#include "kmtypes.h" + + +/** + * The background item of the kmahjongg board. + * @author Christian Krippendorf + */ +class GameBackground : public QGraphicsObject +{ + Q_OBJECT + +public: + /** + * Constructor + * @param item The parent item + */ + explicit GameBackground(QGraphicsObject *item = nullptr); + ~GameBackground() override; + + /** + * Set the background + * @param facePix The pixmap of the face + */ + void setBackground(const QBrush & background); + + /** + * Overrides the paint method of QGraphicsItem + */ + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) override; + + /** + * Set size of element + * @param width Width of element in pixels + * @param height Height of element in pixels + */ + void setSize(qreal width, qreal height); + + /** + * Overrides the boundingRect method of QGraphicsItem + */ + QRectF boundingRect() const override; + + /** + * Returns the rect of the item + * @return The rect of the item + */ + QRectF rect() const; + + /** + * Called in GameView::resizeTileset() before reloading the tiles + */ + void prepareForGeometryChange(); + +private: + QBrush m_background; + + qreal m_width; + qreal m_height; +}; + +#endif // GAMEBACKGROUND_H diff --git a/src/gamebackground.cpp b/src/gamebackground.cpp new file mode 100644 --- /dev/null +++ b/src/gamebackground.cpp @@ -0,0 +1,68 @@ +/* Copyright (C) 2019 Christian Krippendorf + * + * Kmahjongg is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +// own +#include "gamebackground.h" + +// Qt +#include +#include +#include +#include + + +GameBackground::GameBackground(QGraphicsObject * item) + : QGraphicsObject(item) + , m_width(100) + , m_height(100) +{ +} + +GameBackground::~GameBackground() +{ +} + +void GameBackground::setSize(qreal width, qreal height) +{ + m_width = width; + m_height = height; +} + +void GameBackground::prepareForGeometryChange() +{ + prepareGeometryChange(); +} + +void GameBackground::paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) +{ + painter->fillRect(QRectF(0, 0, m_width, m_height), m_background); +} + +void GameBackground::setBackground(const QBrush & background) +{ + m_background = background; +} + +QRectF GameBackground::boundingRect() const +{ + return QRectF(QPointF(0.0, 0.0), QSizeF(m_width, m_height)); +} + +QRectF GameBackground::rect() const +{ + return boundingRect(); +} diff --git a/src/gameitem.cpp b/src/gameitem.cpp --- a/src/gameitem.cpp +++ b/src/gameitem.cpp @@ -71,13 +71,11 @@ bool GameItem::isShadow(QPointF const position) const { - // Get the realated point. - QPointF mappedPosition = mapFromParent(position); + int newPosX = position.x() + getShadowDeltaX(); + int newPosY = position.y() + getShadowDeltaY(); - int newPosX = mappedPosition.x() + getShadowDeltaX(); - int newPosY = mappedPosition.y() + getShadowDeltaY(); - - if ((newPosX < 0 || newPosX > m_selPix->width()) || (newPosY < 0 || newPosY > m_selPix->height())) { + if ((newPosX < 0 || newPosX > m_selPix->width()) || + (newPosY < 0 || newPosY > m_selPix->height())) { return true; } @@ -123,11 +121,11 @@ void GameItem::paint(QPainter * pPainter, const QStyleOptionGraphicsItem *, QWidget *) { if (isSelected()) { - pPainter->drawPixmap(pos(), *m_selPix); - pPainter->drawPixmap(pos() + m_faceOffset, *m_facePix); + pPainter->drawPixmap(QPointF(0.0, 0.0), *m_selPix); + pPainter->drawPixmap(QPointF(0.0, 0.0) + m_faceOffset, *m_facePix); } else { - pPainter->drawPixmap(pos(), *m_unselPix); - pPainter->drawPixmap(pos() + m_faceOffset, *m_facePix); + pPainter->drawPixmap(QPointF(0.0, 0.0), *m_unselPix); + pPainter->drawPixmap(QPointF(0.0, 0.0) + m_faceOffset, *m_facePix); } } @@ -172,7 +170,7 @@ QRectF GameItem::boundingRect() const { - return QRectF(pos(), m_selPix->size()); + return QRectF(QPointF(0.0, 0.0), m_selPix->size()); } QRectF GameItem::rect() const diff --git a/src/gameremovedtiles.h b/src/gameremovedtiles.h new file mode 100644 --- /dev/null +++ b/src/gameremovedtiles.h @@ -0,0 +1,139 @@ +/* Copyright (C) 2019 Christian Krippendorf + * + * Kmahjongg is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef GAMEREMOVEDTILES_H +#define GAMEREMOVEDTILES_H + +// Qt +#include +#include +#include + +// KMahjongg +#include "kmtypes.h" + +// Forward declarations +class KMahjonggTileset; +class GameData; + + +/** + * A QGraphicsObject for representing the removed tiles of the current game. + * @author Christian Krippendorf + */ +class GameRemovedTiles : public QGraphicsObject +{ + Q_OBJECT + +public: + /** + * Constructor + * @param item The parent item + */ + explicit GameRemovedTiles(QGraphicsObject *item = nullptr); + ~GameRemovedTiles() override; + + /** + * Overrides the paint method of QGraphicsItem + */ + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) override; + + /** + * Set size of element + * @param width Width of element in pixels + * @param height Height of element in pixels + */ + void setSize(qreal width, qreal height); + + /** + * Overrides the boundingRect method of QGraphicsItem + */ + QRectF boundingRect() const override; + + /** + * Returns the rect of the item + * @return The rect of the item + */ + QRectF rect() const; + + /** + * Called in GameView::resizeTileset() before reloading the tiles + */ + void prepareForGeometryChange(); + + /** + * Add an removed tile. + * @param itemPos POSITION object of the item. + */ + void addItem(const POSITION & itemPos); + + /** + * Set the game data object. + * @param gameData the game data object. + */ + void setGameData(GameData * gameData); + + /** + * Remove a tile. + */ + void removeLastItem(); + + /** + * Set the tileset for the tile pixmaps. + * @param tiles KMahjonggTileset object. + */ + void setTileset(KMahjonggTileset * tiles); + + /** + * Calculate all values that neccessary to paint all tiles. + */ + void updateTileCalculations(); + + /** + * Reset system for empty game. + */ + void reset(); + + /** + * Remove the last two added tiles. + */ + void undo(); + +private: + qreal m_width; + qreal m_height; + qreal m_borderWidthFrac; + qreal m_tileScale; + qreal m_titleHeightFrac; + qreal m_borderWidthPixel; + qreal m_titleHeightPixel; + qreal m_tileSpaceRow; + qreal m_tileSpaceCol; + qreal m_tileFaceWidth; + qreal m_tileFaceHeight; + qreal m_faceScale; + qreal m_tileFaceWidthScaled; + qreal m_tileFaceHeightScaled; + unsigned int m_maxTilesRow; + unsigned int m_maxTilesCol; + + QList * m_itemFaces; + KMahjonggTileset * m_tiles; + GameData * m_gameData; +}; + +#endif // GAMEREMOVEDTILES_H diff --git a/src/gameremovedtiles.cpp b/src/gameremovedtiles.cpp new file mode 100644 --- /dev/null +++ b/src/gameremovedtiles.cpp @@ -0,0 +1,221 @@ +/* Copyright (C) 2019 Christian Krippendorf + * + * Kmahjongg is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +// own +#include "gameremovedtiles.h" +#include "kmahjonggtileset.h" +#include "gamedata.h" + +// Qt +#include +#include +#include +#include + +#include + + +GameRemovedTiles::GameRemovedTiles(QGraphicsObject * object) + : QGraphicsObject(object) + , m_width(100) + , m_height(100) + , m_borderWidthFrac(0.05) + , m_tileScale(0.9) + , m_titleHeightFrac(0.1) + , m_borderWidthPixel(0) + , m_titleHeightPixel(0) + , m_tileSpaceRow(0) + , m_tileSpaceCol(0) + , m_tileFaceWidth(0) + , m_tileFaceHeight(0) + , m_faceScale(1.0) + , m_tileFaceWidthScaled(0) + , m_tileFaceHeightScaled(0) + , m_maxTilesRow(0) + , m_maxTilesCol(0) + , m_itemFaces(new QList()) + , m_tiles(nullptr) + , m_gameData(nullptr) +{ +} + +GameRemovedTiles::~GameRemovedTiles() +{ + delete m_itemFaces; +} + +void GameRemovedTiles::setSize(qreal width, qreal height) +{ + m_width = width; + m_height = height; +} + +void GameRemovedTiles::setTileset(KMahjonggTileset * tiles) +{ + m_tiles = tiles; +} + +void GameRemovedTiles::prepareForGeometryChange() +{ + prepareGeometryChange(); +} + +void GameRemovedTiles::setGameData(GameData * gameData) +{ + m_gameData = gameData; +} + +void GameRemovedTiles::updateTileCalculations() +{ + int maxTilesRow = 0; + int maxTilesCol = 0; + + // Get the height and the width of the face tile. This has to be multiplied + // by two, cause the value is related to half tile. (half positioning) + m_tileFaceWidth = m_tiles->qWidth() * 2.0; + m_tileFaceHeight = m_tiles->qHeight() * 2.0; + m_tileFaceWidthScaled = m_tileFaceWidth * m_faceScale; + m_tileFaceHeightScaled = m_tileFaceHeight * m_faceScale; + + m_borderWidthPixel = m_borderWidthFrac * m_width; + m_titleHeightPixel = m_titleHeightFrac * m_height; + + maxTilesRow = static_cast( + (m_width - 2 * m_borderWidthPixel) / m_tileFaceWidthScaled + ); + maxTilesCol = static_cast( + (m_height - 2 * m_borderWidthPixel - m_titleHeightPixel) / + m_tileFaceHeightScaled + ); + + m_tileSpaceRow = ((m_width - 2 * m_borderWidthPixel) - + maxTilesRow * m_tileFaceWidthScaled) / (maxTilesRow - 1); + m_tileSpaceCol = ((m_height - + 2 * m_borderWidthPixel - m_titleHeightPixel) - + maxTilesCol * m_tileFaceHeightScaled) / (maxTilesCol - 1); + + m_maxTilesRow = maxTilesRow; + m_maxTilesCol = maxTilesCol; +} + +void GameRemovedTiles::paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) +{ + updateTileCalculations(); + + // General painter settings. + painter->setRenderHint(QPainter::Antialiasing); + + // Paint the background. + painter->setOpacity(0.5); + QPainterPath path; + path.addRoundedRect(QRectF(0, 0, m_width, m_height), 10, 10); + painter->fillPath(path, Qt::black); + + // Paint the title text. + painter->setPen(Qt::white); + QFont font(painter->font()); + font.setPointSize(m_titleHeightPixel * 0.15); + painter->setFont(font); + painter->drawText( + QRectF(m_borderWidthPixel, m_borderWidthPixel, m_width, m_titleHeightPixel), + i18n("Removed tiles") + ); + + // Exit if no tileset has been set to this object. + if (m_tiles == nullptr || m_itemFaces->isEmpty()) { + return; + } + + // Paint all the tiles. + painter->setPen(QPen(Qt::white, 10)); + + unsigned int row = 0; + unsigned int col = 0; + int start = m_itemFaces->size() - (m_maxTilesCol * m_maxTilesRow * 2); + if (start < 0) { + start *= 0; + } + for (int pos = start; pos < m_itemFaces->size() - 1; pos+=2) { + if (col >= m_maxTilesRow) { + row++; + col = 0; + } + + // Get the pixmap of the face. + QPixmap face; + face = m_tiles->tileface(m_itemFaces->at(pos)); + face = face.scaledToHeight( + m_tileFaceHeightScaled, Qt::SmoothTransformation + ); + + // Paint the background of the face. + QPainterPath pixPath; + pixPath.addRoundedRect( + QRectF( + m_borderWidthPixel + col * m_tileSpaceRow + col * m_tileFaceWidth, + m_titleHeightPixel + row * m_tileSpaceCol + row * m_tileFaceHeight, + m_tileFaceWidth, m_tileFaceHeight + ), 10, 10 + ); + painter->setOpacity(1.0 - (m_itemFaces->size() - pos) / 100.0); + painter->fillPath(pixPath, Qt::white); + + // Paint the pixmap of the face. + painter->setOpacity(1.0 - (m_itemFaces->size() - pos) / 100.0); + painter->drawPixmap( + QPointF( + m_borderWidthPixel + col * m_tileSpaceRow + col * m_tileFaceWidth, + m_titleHeightPixel + row * m_tileSpaceCol + row * m_tileFaceHeight + ), face + ); + + col++; + } +} + +void GameRemovedTiles::undo() +{ + if (m_itemFaces->size() >= 2) { + m_itemFaces->removeLast(); + m_itemFaces->removeLast(); + } +} + +void GameRemovedTiles::reset() +{ + m_itemFaces->clear(); +} + +QRectF GameRemovedTiles::boundingRect() const +{ + return QRectF(QPointF(0.0, 0.0), QSizeF(m_width, m_height)); +} + +QRectF GameRemovedTiles::rect() const +{ + return boundingRect(); +} + +void GameRemovedTiles::addItem(const POSITION & itemPos) +{ + m_itemFaces->append(itemPos.f); +} + +void GameRemovedTiles::removeLastItem() +{ + m_itemFaces->removeLast(); +} diff --git a/src/gamescene.h b/src/gamescene.h --- a/src/gamescene.h +++ b/src/gamescene.h @@ -31,6 +31,8 @@ class GameData; class GameWidget; class KMahjonggLayout; +class GameBackground; +class GameRemovedTiles; /** * Holds and manages all GameItems. @@ -76,6 +78,11 @@ * Override from QGraphicsScene. */ void clear(); + /** + * Clear only the GameItem objects + */ + void clearGameItems(); + /** * Override from QGraphicsScene. */ void addItem(GameItem * gameItem); @@ -118,6 +125,18 @@ * @return True if selectable else false. */ bool isSelectable(const GameItem * const pameItem) const; + /** + * Set a background + * @param gameBackground The background object + */ + void setBackgroundItem(GameBackground * gameBackground); + + /** + * Set a removedtiles item + * @param gameRemovedTiles The removedtiles object + */ + void setRemovedTilesItem(GameRemovedTiles * gameRemovedTiles); + signals: void rotateCW(); void rotateCCW(); @@ -137,6 +156,9 @@ GameItem * m_pGameItemsArray[BOARD_WIDTH][BOARD_HEIGHT][BOARD_DEPTH]; GameItem * m_pFirstSelectedItem; GameItem * m_pSecondSelectedItem; + + GameBackground * m_gameBackground; + GameRemovedTiles * m_gameRemovedTiles; }; #endif // GAMESCENE_H diff --git a/src/gamescene.cpp b/src/gamescene.cpp --- a/src/gamescene.cpp +++ b/src/gamescene.cpp @@ -24,11 +24,16 @@ #include "gameitem.h" #include "gameview.h" #include "kmahjongglayout.h" +#include "gamebackground.h" +#include "gameremovedtiles.h" + GameScene::GameScene(QObject * parent) : QGraphicsScene(parent) , m_pFirstSelectedItem(nullptr) , m_pSecondSelectedItem(nullptr) + , m_gameBackground(nullptr) + , m_gameRemovedTiles(nullptr) { initializeGameItemsArray(); } @@ -37,6 +42,22 @@ { } +void GameScene::clearGameItems() +{ + // Remove all GameItem objects and DON'T remove GameBackground object. + QList items = QGraphicsScene::items(); + for (int i = 0; i < items.size(); i++) { + GameItem *gameItem = dynamic_cast(items.at(i)); + if (gameItem != nullptr) { + QGraphicsScene::removeItem(gameItem); + } + } + initializeGameItemsArray(); + + m_pFirstSelectedItem = nullptr; + m_pSecondSelectedItem = nullptr; +} + void GameScene::clear() { QGraphicsScene::clear(); @@ -58,6 +79,28 @@ } } +void GameScene::setRemovedTilesItem(GameRemovedTiles * gameRemovedTiles) +{ + // If a removedtiles object already exist, delete it from scene + if (m_gameRemovedTiles != nullptr) { + QGraphicsScene::removeItem(m_gameRemovedTiles); + } + + m_gameRemovedTiles = gameRemovedTiles; + QGraphicsScene::addItem(gameRemovedTiles); +} + +void GameScene::setBackgroundItem(GameBackground * gameBackground) +{ + // If a background exist, delete it from scene + if (m_gameBackground != nullptr) { + QGraphicsScene::removeItem(m_gameBackground); + } + + m_gameBackground = gameBackground; + QGraphicsScene::addItem(gameBackground); +} + void GameScene::addItem(GameItem * gameItem) { QGraphicsScene::addItem(gameItem); @@ -139,7 +182,10 @@ QList tmpList; for (int i = 0; i < originalList.size(); ++i) { - tmpList.append(dynamic_cast(originalList.at(i))); + GameItem * gameItem = dynamic_cast(originalList.at(i)); + if (gameItem != nullptr) { + tmpList.append(gameItem); + } } return tmpList; @@ -202,15 +248,21 @@ void GameScene::mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent) { // N.B. Event occurs when there is a click OR double-click with ANY button. - GameItem * gameItem = dynamic_cast(itemAt(mouseEvent->scenePos().x(), - mouseEvent->scenePos().y(), QTransform())); + GameItem * gameItem = dynamic_cast( + itemAt(mouseEvent->scenePos().x(), + mouseEvent->scenePos().y(), QTransform()) + ); // An item was clicked. if (gameItem != nullptr) { - // If we click on a shadow of the actual item, we have to correct the clicking position, in - // order to simulate a transparent shadow. + // If we click on a shadow of the actual item, we have to correct the + // clicking position, in order to simulate a transparent shadow. if (gameItem->isShadow(mouseEvent->scenePos() - gameItem->pos())) { - gameItem = dynamic_cast(itemAt(mouseEvent->scenePos().x() + gameItem->getShadowDeltaX(), mouseEvent->scenePos().y() + gameItem->getShadowDeltaY(), QTransform())); + gameItem = dynamic_cast( + itemAt(mouseEvent->scenePos().x() + gameItem->getShadowDeltaX(), + mouseEvent->scenePos().y() + gameItem->getShadowDeltaY(), + QTransform()) + ); } } diff --git a/src/gameview.h b/src/gameview.h --- a/src/gameview.h +++ b/src/gameview.h @@ -27,6 +27,8 @@ class GameScene; class GameData; class GameItem; +class GameBackground; +class GameRemovedTiles; class SelectionAnimation; class MoveListAnimation; class DemoAnimation; @@ -59,6 +61,11 @@ * @param gameItems The items of which the positions should be updated. */ void updateItemsPosition(const QList &gameItems); + /** + * Overloaded function of scene game item positioning. + */ + void updateItemsPosition(); + /** * Updates the whole widget. * @@ -67,7 +74,13 @@ /** * Override from QGraphcisView. */ - virtual QList items() const; + virtual QList getGameItems() const; + + /** + * Set wether removed tiles should be shown. + * @param show True if removed tiles should be shown. + */ + void showRemovedTiles(bool show); /** * Override from QGraphicsView. */ @@ -397,9 +410,14 @@ bool m_gamePaused; bool m_match; bool m_gameGenerated; + bool m_showRemovedTiles; + + qreal m_remTilesWidthFactor; GameData * m_gameData; GameItem * m_selectedItem; + GameBackground * m_gameBackground; + GameRemovedTiles * m_gameRemovedTiles; QString * m_tilesetPath; QString * m_backgroundPath; diff --git a/src/gameview.cpp b/src/gameview.cpp --- a/src/gameview.cpp +++ b/src/gameview.cpp @@ -36,6 +36,9 @@ #include "movelistanimation.h" #include "prefs.h" #include "selectionanimation.h" +#include "gamebackground.h" +#include "gameremovedtiles.h" + GameView::GameView(GameScene * gameScene, GameData * gameData, QWidget * parent) : QGraphicsView(gameScene, parent) @@ -44,8 +47,12 @@ , m_gamePaused(false) , m_match(false) , m_gameGenerated(false) + , m_showRemovedTiles(true) + , m_remTilesWidthFactor(0.3) , m_gameData(gameData) , m_selectedItem(nullptr) + , m_gameBackground(new GameBackground()) + , m_gameRemovedTiles(new GameRemovedTiles()) , m_tilesetPath(new QString()) , m_backgroundPath(new QString()) , m_helpAnimation(new SelectionAnimation(this)) @@ -62,6 +69,9 @@ // Read in some settings. m_angle = static_cast(Prefs::angle()); + // Show removed tiles. + m_showRemovedTiles = Prefs::removedTiles(); + // Init HelpAnimation m_helpAnimation->setAnimationSpeed(ANIMATION_SPEED); m_helpAnimation->setRepetitions(3); @@ -72,6 +82,16 @@ // Init MoveListAnimation m_moveListAnimation->setAnimationSpeed(ANIMATION_SPEED); + // Set the tileset to the game removed tiles object. + m_gameRemovedTiles->setTileset(m_tiles); + m_gameRemovedTiles->setGameData(m_gameData); + + // Add the fix background item to the scene + scene()->setBackgroundItem(m_gameBackground); + + // Add the fix removedtiles item to the scene + scene()->setRemovedTilesItem(m_gameRemovedTiles); + m_selectionChangedConnect = connect(scene(), &GameScene::selectionChanged, this, &GameView::selectionChanged); connect(m_demoAnimation, &DemoAnimation::changeItemSelectedState, this, &GameView::changeItemSelectedState); @@ -88,6 +108,7 @@ delete m_helpAnimation; delete m_demoAnimation; delete m_moveListAnimation; + delete m_gameBackground; delete m_background; delete m_backgroundPath; delete m_tilesetPath; @@ -136,6 +157,9 @@ ++m_gameData->m_allowRedo; + // Undo removed tile object. + m_gameRemovedTiles->undo(); + setStatusText(i18n("Undo operation done successfully.")); return true; @@ -189,6 +213,9 @@ checkDemoAnimationActive(true); checkMoveListAnimationActive(true); + // Reset the removed tile object. + m_gameRemovedTiles->reset(); + // Create a random game number, if no one was given. if (gameNumber == -1) { m_gameNumber = KRandom::random(); @@ -219,6 +246,9 @@ addItemsFromBoardLayout(); populateItemNumber(); + QResizeEvent event(size(), size()); + resizeEvent(&event); + setStatusText(i18n("Ready. Now it is your turn.")); return; @@ -229,7 +259,7 @@ m_gameGenerated = false; // Hide all generated tiles. - foreach (GameItem * item, items()) { + foreach (GameItem * item, getGameItems()) { item->hide(); } @@ -309,6 +339,10 @@ // Put an empty item in the data object. (data part) m_gameData->putTile(stItemPos.z, stItemPos.y, stItemPos.x, 0); + // Add the tile to the removedtiles object. + m_gameRemovedTiles->addItem(stItemPos); + m_gameRemovedTiles->update(); + // Remove the item from the scene object. (graphic part) scene()->removeItem(stItemPos); @@ -465,11 +499,11 @@ void GameView::pause(bool isPaused) { if (isPaused) { - foreach (GameItem * item, items()) { + foreach (GameItem * item, getGameItems()) { item->hide(); } } else { - foreach (GameItem * item, items()) { + foreach (GameItem * item, getGameItems()) { item->show(); } } @@ -496,7 +530,7 @@ m_gameData->shuffle(); // Update the item images. - updateItemsImages(items()); + updateItemsImages(getGameItems()); // Cause of using the shuffle function... increase the cheat used variable. m_cheatsUsed += 15; @@ -527,8 +561,8 @@ // The signal is reconnected at the end of the function. disconnect(m_selectionChangedConnect); - // Remove all existing items. - scene()->clear(); + // Remove all GameItem objects + scene()->clearGameItems(); // Create the items and add them to the scene. for (int iZ = 0; iZ < m_gameData->m_depth; ++iZ) { @@ -550,7 +584,7 @@ } } - updateItemsImages(items()); + updateItemsImages(getGameItems()); updateItemsOrder(); // Reconnect our selectionChanged() slot. @@ -600,26 +634,61 @@ addItem(stItemPos, true, true, true); } +void GameView::showRemovedTiles(bool show) +{ + if (m_showRemovedTiles != show) { + m_showRemovedTiles = show; + m_gameRemovedTiles->setVisible(show); + + QResizeEvent event(size(), size()); + resizeEvent(&event); + updateItemsPosition(); + } +} + +void GameView::updateItemsPosition() +{ + updateItemsPosition(scene()->items()); +} + void GameView::updateItemsPosition(const QList &gameItems) { + // The width and height need to be corrected, related to the removedtiles + // view and wether it is shown or not. For now the removed tiles field can + // only be placed to the right of the board. + qreal boardWidth = (!m_showRemovedTiles) ? width() : width() * (1 - m_remTilesWidthFactor - 0.05); + qreal boardHeight = height(); + + // TODO: Change!!! + // Make decision of painting the removed tiles. + // These factor are needed for the different angles. So we simply can // calculate to move the items to the left or right and up or down. int angleXFactor = (m_angle == NE || m_angle == SE) ? -1 : 1; int angleYFactor = (m_angle == NW || m_angle == NE) ? -1 : 1; // Get half width and height of tile faces: minimum spacing = 1 pixel. - qreal tileWidth = m_tiles->qWidth() + 0.5; - qreal tileHeight = m_tiles->qHeight() + 0.5; + // NOTE - qWidth is devided by 2 in kmahjonggtileset.cpp. The reason is + // unknown for now. Please review this later. + qreal tileFaceWidth = m_tiles->qWidth() * 2 + 1; + qreal tileFaceHeight = m_tiles->qHeight() * 2 + 1; // Get half height and width of tile-layout: ((n - 1) faces + full tile)/2. - qreal tilesWidth = tileWidth * (m_gameData->m_width - 2) / 2 - + m_tiles->width() / 2; - qreal tilesHeight = tileHeight * (m_gameData->m_height - 2) / 2 - + m_tiles->height() / 2; + + // Because the positions of the tiles can be half-positioned, the width and + // height in GameData is two times higher. To get the maximum number of + // tiles in a row or column, the GameData-width and -height have to be + // devided by two. + qreal numTilesX = m_gameData->m_width / 2; + qreal numTilesY = m_gameData->m_height / 2; + + // Calculate the total width and height of board layout with tiles. + qreal tilesWidth = tileFaceWidth * numTilesX + m_tiles->levelOffsetX(); + qreal tilesHeight = tileFaceHeight * numTilesY + m_tiles->levelOffsetY(); // Get the top-left offset required to center the items in the view. - qreal xFrame = (width() / 2 - tilesWidth) / 2; - qreal yFrame = (height() / 2 - tilesHeight) / 2; + qreal xFrame = (boardWidth - tilesWidth) / 2; + qreal yFrame = (boardHeight - tilesHeight) / 2; // TODO - The last /2 makes it HALF what it should be, but it gets doubled // somehow before the view is painted. Why? Apparently it is because @@ -627,8 +696,11 @@ // being an item in the scene and filling the scene completely. So // the whole scene is just the rectangle that contains the tiles. // NOTE - scene()->itemsBoundingRect() returns the correct doubled offset. - - for (int i = 0; i < gameItems.size(); ++i) { + // NOTE - insert game background object but problem persist + // SOLVED - Problem was in coordinate system in QGraphicsItem/GameItem. + // Painting positions are relative, but were defined absolute to + // scene coordinate system. Same with boundingRect(). + for (int i = 0; i < gameItems.size(); i++) { GameItem * gameItem = gameItems.at(i); // Get rasterized positions of the item. @@ -639,11 +711,17 @@ // Set the position of the item on the scene. gameItem->setPos( - xFrame + tileWidth * x / 2 - + z * angleXFactor * (m_tiles->levelOffsetX() / 2), - yFrame + tileHeight * y / 2 - + z * angleYFactor * (m_tiles->levelOffsetY() / 2)); + xFrame + tileFaceWidth / 2 * x + + z * angleXFactor * (m_tiles->levelOffsetX()), + yFrame + tileFaceHeight / 2 * y + + z * angleYFactor * (m_tiles->levelOffsetY())); } + + // Position the removedtiles object. + qreal removedTilesBorder = height() * 0.1 / 2; + m_gameRemovedTiles->setPos( + width() * (1 - m_remTilesWidthFactor), removedTilesBorder + ); } void GameView::updateItemsOrder() @@ -703,7 +781,7 @@ } } - updateItemsPosition(items()); + updateItemsPosition(getGameItems()); } void GameView::orderLine(GameItem * startItem, int xStart, int xEnd, int xCounter, int y, int yCounter, int z, int & zCount) @@ -786,7 +864,7 @@ void GameView::setAngle(TileViewAngle angle) { m_angle = angle; - updateItemsImages(items()); + updateItemsImages(getGameItems()); updateItemsOrder(); } @@ -812,7 +890,7 @@ break; } - updateItemsImages(items()); + updateItemsImages(getGameItems()); updateItemsOrder(); } @@ -833,17 +911,20 @@ break; } - updateItemsImages(items()); + updateItemsImages(getGameItems()); updateItemsOrder(); } -QList GameView::items() const +QList GameView::getGameItems() const { - QList originalList = QGraphicsView::items(); + QList items = QGraphicsView::items(); QList tmpList; - for (int i = 0; i < originalList.size(); ++i) { - tmpList.append(dynamic_cast(originalList.at(i))); + for (int i = 0; i < items.size(); ++i) { + GameItem *gameItem = dynamic_cast(items.at(i)); + if (nullptr != gameItem) { + tmpList.append(gameItem); + } } return tmpList; @@ -876,8 +957,28 @@ return; } - resizeTileset(event->size()); + // The size need to be corrected, related to the removedtiles + // view and wether it is shown or not. For now the removed tiles field can + // only be placed to the right of the board. + QSize size(event->size()); + if (m_showRemovedTiles) { + resizeTileset(QSize( + size.width() * (1 - m_remTilesWidthFactor - 0.05), size.height() + )); + + // Update removed tiles + qreal removedTilesBorder = size.height() * 0.1 / 2; + m_gameRemovedTiles->setSize( + size.width() * m_remTilesWidthFactor - removedTilesBorder, + size.height() * 0.9 + ); + } else { + resizeTileset(QSize( + size.width(), size.height() + )); + } + // Update background m_background->sizeChanged(width(), height()); updateBackground(); @@ -890,16 +991,18 @@ return; } - QSize newtiles = m_tiles->preferredTileSize(size, m_gameData->m_width / 2, m_gameData->m_height / 2); + QSize newtiles = m_tiles->preferredTileSize( + size, m_gameData->m_width / 2, m_gameData->m_height / 2 + ); - foreach (GameItem * item, items()) { + foreach (GameItem * item, getGameItems()) { item->prepareForGeometryChange(); } m_tiles->reloadTileset(newtiles); - updateItemsImages(items()); - updateItemsPosition(items()); + updateItemsImages(getGameItems()); + updateItemsPosition(getGameItems()); } void GameView::updateItemsImages(const QList &gameItems) @@ -938,11 +1041,8 @@ void GameView::updateBackground() { - // qCDebug(KMAHJONGG_LOG) << "Update the background"; - // TODO - The background should be a scene-item? See updateItemsPosition(). - - QBrush brush(m_background->getBackground()); - setBackgroundBrush(brush); + m_gameBackground->setBackground(m_background->getBackground()); + m_gameBackground->setSize(width(), height()); } void GameView::setGameData(GameData * gameData) diff --git a/src/kmahjongg.cpp b/src/kmahjongg.cpp --- a/src/kmahjongg.cpp +++ b/src/kmahjongg.cpp @@ -303,6 +303,9 @@ qCDebug(KMAHJONGG_LOG) << "An error occurred when loading the background " << Prefs::background() << " KMahjongg will continue with the default background."; } + // Set wether removed tiles should be shown. + m_gameView->showRemovedTiles(Prefs::removedTiles()); + // Maybe load a new layout and start a new game if the layout or random mode has changed. if (m_boardLayout->path() != Prefs::layout() || m_bLastRandomSetting != Prefs::randomLayout()) { // The boardlayout path will likely not be the same as the preference setting if diff --git a/src/kmahjongg.kcfg b/src/kmahjongg.kcfg --- a/src/kmahjongg.kcfg +++ b/src/kmahjongg.kcfg @@ -17,6 +17,10 @@ 0 + + + false + false diff --git a/src/settings.ui b/src/settings.ui --- a/src/settings.ui +++ b/src/settings.ui @@ -30,6 +30,13 @@ + + + + Show removed tiles + + +