Adding icon
[impuzzle] / src / gameview.cpp
1 /*
2   Image Puzzle - A set your pieces straight game
3   Copyright (C) 2009  Timo Härkönen
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (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 "gameview.h"
20 #include "puzzleitem.h"
21 #include "defines.h"
22 #include "introitem.h"
23
24 #include <QGraphicsScene>
25 #include <QDateTime>
26 #include <QTimer>
27 #include <QPropertyAnimation>
28 #include <QParallelAnimationGroup>
29 #include <QFont>
30 #include <QMessageBox>
31
32 #include <QDebug>
33
34 GameView *GameView::instance_ = 0;
35
36 GameView::GameView(QWidget *parent) :
37         QGraphicsView(parent)
38 {
39     scene_ = new QGraphicsScene;
40     hiddenIndex_ = -1;
41     setScene(scene_);
42
43     introItem_ = new IntroItem;
44     introItem_->setText("Select new game from menu to play");
45     scene_->addItem(introItem_);
46
47     verticalStep_ = 0;
48     horizontalStep_ = 0;
49
50     qsrand(QDateTime::currentDateTime().toTime_t());
51 }
52
53 GameView *GameView::instance()
54 {
55     if(!instance_) {
56         instance_ = new GameView;
57     }
58
59     return instance_;
60 }
61
62 QList<PuzzleItem *> GameView::pieces() const
63 {
64     return pieces_;
65 }
66
67 void GameView::setPieces(const QList<PuzzleItem *> pieces)
68 {
69     if(pieces.isEmpty()) {
70         qDebug() << "Empty list @ GameView::setPieces";
71         return;
72     }
73
74     QList<QGraphicsItem *> previousItems = scene_->items();
75     if(!previousItems.isEmpty()) {
76         foreach(QGraphicsItem *item, previousItems) {
77             scene_->removeItem(item);
78         }
79     }
80
81     pieces_ = pieces;
82
83     int horizontalCount = 0;
84
85     // Find out board size
86     if(pieces_.count() == EASY_PIECE_COUNT) {
87         horizontalCount = EASY_HORIZONTAL_COUNT;
88     }
89     else if(pieces_.count() == HARD_PIECE_COUNT) {
90         horizontalCount = HARD_HORIZONTAL_COUNT;
91     }
92     else {
93         qDebug() << "Invalid piece count @ GameView::setPieces";
94         qDebug() << QString("Count was %1").arg(pieces_.count());
95         return;
96     }
97
98     int verticalCount = pieces_.count() / horizontalCount;
99     horizontalStep_ = IMAGE_WIDTH / horizontalCount + 5;
100     verticalStep_ = IMAGE_HEIGHT / verticalCount + 5;
101
102     int pieceNumber = 0;
103
104     // Set pieces to their correct positions
105     for(int i = 0; i < verticalCount; ++i) {
106         for(int j = 0; j < horizontalCount; ++j) {
107             scene_->addItem(pieces_.at(pieceNumber));
108             QPointF point(j * horizontalStep_, i * verticalStep_);
109             pieces_.at(pieceNumber)->setPos(point);
110             pieces_.at(pieceNumber)->setCorrectPlace(point);
111             pieces_.at(pieceNumber)->setCurrentPlace(point);
112             pieces_.at(pieceNumber)->setDrawNumber(true);
113             pieceNumber++;
114         }
115     }
116
117     // Wait
118     QTimer::singleShot(750, this, SLOT(shufflePieces()));
119 }
120
121 void GameView::shufflePieces()
122 {
123     if(pieces_.isEmpty()) {
124         qDebug() << "Empty list @ GameView::shufflePieces";
125         return;
126     }
127
128     // Give pieces ramdom locations
129     int rounds = 5; //TODO
130     for(int j = 0; j < rounds; ++j) {
131         for(int i = 0; i < pieces_.count(); ++i) {
132             QPointF tmp;
133             int changeIndex = 0;
134             while(changeIndex == i) {
135                 changeIndex = qrand() % pieces_.count();
136             }
137             tmp = pieces_.at(changeIndex)->currentPlace();
138             pieces_.at(changeIndex)->setCurrentPlace(pieces_.at(i)->currentPlace());
139             pieces_.at(i)->setCurrentPlace(tmp);
140         }
141     }
142
143     // TODO Animate transitions to new locations
144     QParallelAnimationGroup *animationGroup = new QParallelAnimationGroup(this);
145     for(int i = 0; i < pieces_.count(); ++i) {
146         QPropertyAnimation *animation = new QPropertyAnimation(pieces_.at(i), "pos");
147         animation->setStartValue(pieces_.at(i)->correctPlace());
148         animation->setEndValue(pieces_.at(i)->currentPlace());
149         animation->setDuration(750);
150         animation->setEasingCurve(QEasingCurve::InOutCirc);
151         animationGroup->addAnimation(animation);
152     }
153     animationGroup->start();
154
155     // Hide random piece
156     int hiddenPiece = qrand() % pieces_.count();
157     emptyPlace_ = pieces_.at(hiddenPiece)->currentPlace();
158     pieces_.at(hiddenPiece)->hide();
159     hiddenIndex_ = hiddenPiece;
160
161     setMovingPieces();
162 }
163
164 QPointF GameView::emptyPlace()
165 {
166     return emptyPlace_;
167 }
168
169 void GameView::setEmptyPlace(const QPointF &place)
170 {
171     emptyPlace_ = place;
172 }
173
174 bool GameView::areAllPiecesOk() const
175 {
176     for(int i = 0; i < pieces_.count(); ++i) {
177         // Skip hidden piece
178         if(i == hiddenIndex_) {
179             continue;
180         }
181         // Id piece is not in it's place
182         else if(pieces_.at(i)->correctPlace() != pieces_.at(i)->currentPlace()) {
183             return false;
184         }
185     }
186     // Show hidden piece and move it to it's place
187     pieces_.at(hiddenIndex_)->show();
188     pieces_.at(hiddenIndex_)->moveMeTo(emptyPlace_);
189
190     // Set all pieces not movable and hide numbers
191     for(int i = 0; i < pieces_.count(); ++i) {
192         pieces_.at(i)->setMovable(false);
193         pieces_.at(i)->setDrawNumber(false);
194     }
195
196     // Show dialog with move count
197     QMessageBox::about(const_cast<GameView *>(this), tr("You won"), QString("Puzzle completed with %1 moves").arg(PuzzleItem::moveCount()));
198
199     return true;
200 }
201
202 void GameView::setMovingPieces()
203 {
204     if(pieces_.isEmpty()) {
205         qDebug() << "Empty list @ GameView::setMovingPieces";
206         return;
207     }
208
209     QPointF point = QPointF();
210     for(int i = 0; i < pieces_.count(); ++i) {
211         point = pieces_.at(i)->currentPlace();
212
213         // Is piece on the left side of the empty space
214         if(emptyPlace_.y() == point.y() && point.x() + horizontalStep_ == emptyPlace_.x()) {
215             pieces_.at(i)->setMovable(true);
216         }
217
218         // Is piece on the right side of the empty space
219         else if(emptyPlace_.y() == point.y() && point.x() - horizontalStep_ == emptyPlace_.x()) {
220             pieces_.at(i)->setMovable(true);
221         }
222
223         // Is piece below the empty space
224         else if(emptyPlace_.x() == point.x() && point.y() - verticalStep_ == emptyPlace_.y()) {
225             pieces_.at(i)->setMovable(true);
226         }
227
228         // Is piece on top of the empty space
229         else if(emptyPlace_.x() == point.x() && point.y() + verticalStep_ == emptyPlace_.y()) {
230             pieces_.at(i)->setMovable(true);
231         }
232
233         // The piece is somewhere else
234         else {
235             pieces_.at(i)->setMovable(false);
236         }
237     }
238 }