bigger label text; update translation
[colorflood] / colorflood / src / field.cpp
1 /*
2   Copyright 2010 Serge Ziryukin <ftrvxmtrx@gmail.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; version 2 of the License.
7
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   GNU General Public License for more details.
12 */
13
14 #include <QMetaType>
15 #include <QDataStream>
16 #include <QSettings>
17 #include <QTime>
18 #include <QPainter>
19 #include <QPaintEvent>
20 #include <QMessageBox>
21 #include "field.hpp"
22 #include "colorscheme.hpp"
23
24 static const int fieldWidth = 420;
25
26 const int Field::numRects[Field::NUM_SIZES] = { 14, 21, 28 };
27 const int Field::numTurns[Field::NUM_SIZES] = { 25, 35, 50 };
28
29 // we declare out QVector<FieldRect> metatype
30 // and stream operators to save whole field to settings
31 Q_DECLARE_METATYPE(Field::RectVector);
32
33 static QDataStream &operator<< (QDataStream &out, const Field::RectVector &rv)
34 {
35     for (QVector<Field::FieldRect>::const_iterator rect = rv.begin();
36          rect != rv.end();
37          rect++)
38     {
39         out << (*rect).brush;
40         out << (*rect).flood;
41     }
42
43     return out;
44 }
45
46 static QDataStream &operator>> (QDataStream &in, Field::RectVector &rv)
47 {
48     Field::FieldRect r;
49
50     rv.clear();
51
52     for (; !in.atEnd() ;)
53     {
54         in >> r.brush >> r.flood;
55         rv << r;
56     }
57
58     rv.pop_back();
59
60     return in;
61 }
62
63 Field::Field (QWidget *parent, int *turns)
64     : QWidget (parent),
65       finished(false)
66 {
67     Q_ASSERT(parent);
68
69     setFixedSize(fieldWidth, fieldWidth);
70
71     // restore field size and field itself from settings
72
73     qRegisterMetaType<RectVector>("Field::RectVector");
74     qRegisterMetaTypeStreamOperators<RectVector>("Field::RectVector");
75
76     QSettings settings;
77
78     int size = settings.value("field/size", SIZE_SMALL).toInt();
79
80     if (size < SIZE_SMALL || size >= NUM_SIZES)
81         size = SIZE_SMALL;
82
83     this->size = (FieldSize)size;
84
85     if (settings.contains("field/data"))
86         data = settings.value("field/data").value<RectVector>();
87
88     this->turns = settings.value("field/turns", 0).toInt();
89
90     if (data.size() != numRects[size] * numRects[size])
91         randomize();
92
93     *turns = this->turns;
94 }
95
96 Field::~Field ()
97 {
98     if (!finished)
99     {
100         QSettings settings;
101
102         settings.setValue("field/size", size);
103
104         QVariant v;
105         v.setValue(data);
106         settings.setValue("field/data", v);
107
108         settings.setValue("field/turns", turns);
109     }
110 }
111
112 Field::FieldSize Field::getSize () const
113 {
114     return size;
115 }
116
117 void Field::randomize ()
118 {
119     FieldRect rect;
120     rect.flood = false;
121
122     data.clear();
123     data = RectVector(numRects[size] * numRects[size], rect);
124
125     qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
126
127     int numBrushes = ColorScheme::instance().getScheme().size();
128
129     for (QVector<FieldRect>::iterator rect = data.begin();
130          rect != data.end();
131          rect++)
132     {
133         (*rect).brush = qrand() % numBrushes;
134     }
135
136     turns = 0;
137     finished = false;
138     emit turnsChanged(turns);
139
140     // flood from top-left
141     data[0].flood = true;
142     floodNeighbours(data[0].brush, 0, 0);
143
144     update();
145 }
146
147 int Field::getNumRectsOfSize (FieldSize size)
148 {
149     return numRects[size];
150 }
151
152 int Field::getNumTurnsOfSize (FieldSize size)
153 {
154     return numTurns[size];
155 }
156
157 int Field::getRectSize (FieldSize size)
158 {
159     return fieldWidth / numRects[size];
160 }
161
162 void Field::tryFloodRecurse (quint8 brush, int x, int y)
163 {
164     FieldRect &rect = data[x + y*numRects[size]];
165
166     if (!rect.flood && rect.brush == brush)
167     {
168         rect.flood = true;
169         floodNeighbours(brush, x, y);
170     }
171 }
172
173 void Field::floodNeighbours (quint8 brush, int x, int y)
174 {
175     int s = numRects[size];
176
177     data[x + y*s].brush = brush;
178
179     if (x > 0)
180         tryFloodRecurse(brush, x - 1, y);
181
182     if (y > 0)
183         tryFloodRecurse(brush, x, y - 1);
184
185     if (x < s - 1)
186         tryFloodRecurse(brush, x + 1, y);
187         
188     if (y < s - 1)
189         tryFloodRecurse(brush, x, y + 1);
190 }
191
192 void Field::mousePressEvent (QMouseEvent *event)
193 {
194     if (event->button() == Qt::LeftButton)
195         randomize();
196 }
197
198 void Field::paintEvent (QPaintEvent *event)
199 {
200     QPainter painter;
201     painter.begin(this);
202
203     QRect rect = QRect(0, 0, getRectSize(size), getRectSize(size));
204
205     const QVector<QBrush> &scheme = ColorScheme::instance().getScheme();
206
207     for (int y = 0; y < numRects[size] ;y++)
208     {
209         int n = y * numRects[size];
210
211         for (int x = 0; x < numRects[size] ;x++, n++)
212         {
213             rect.moveTo(x * rect.width(), y * rect.height());
214
215             if (rect.intersects(event->rect()))
216                 painter.fillRect(rect, scheme.at(data[n].brush));
217         }
218     }
219
220     painter.end();
221 }
222
223 void Field::flood (int colorIndex)
224 {
225     // don't fill with the same color over and over again
226     if (colorIndex == data[0].brush)
227         return;
228
229     if (finished)
230         return;
231
232     emit turnsChanged(++turns);
233
234     // flood with new color
235     for (int y = 0; y < numRects[size] ;y++)
236     {
237         int n = y * numRects[size];
238
239         for (int x = 0; x < numRects[size] ;x++, n++)
240         {
241             if (data[n].flood)
242                 floodNeighbours(colorIndex, x, y);
243         }
244     }
245
246     repaint();
247
248     bool allFlooded = true;
249
250     // check if all field flooded
251     for (QVector<Field::FieldRect>::const_iterator rect = data.begin();
252          rect != data.end();
253          rect++)
254     {
255         if (!(*rect).flood)
256         {
257             allFlooded = false;
258             break;
259         }
260     }
261
262     QString msg;
263
264     if (allFlooded)
265     {
266         finished = true;
267         /*: win message */
268         msg = tr("You won!");
269     }
270     else if (getNumTurnsOfSize(size) == turns)
271     {
272         finished = true;
273         /*: fail message */
274         msg = tr("You lost!");
275     }
276
277     if (finished)
278     {
279         QMessageBox box;
280         box.setText(msg);
281         box.exec();
282         randomize();
283     }
284 }