field.cpp 14 KB
Newer Older
Stephan Kulow's avatar
Stephan Kulow committed
1
2
#include "field.h"

Stephan Kulow's avatar
Stephan Kulow committed
3
#include <qdrawutil.h>
Nicolas Hadacek's avatar
Nicolas Hadacek committed
4
#include <qlayout.h>
Nicolas Hadacek's avatar
Nicolas Hadacek committed
5
#include <qbitmap.h>
Stephan Kulow's avatar
Stephan Kulow committed
6

Hans Petter Bieker's avatar
Hans Petter Bieker committed
7
#include <klocale.h>
Stephan Kulow's avatar
Stephan Kulow committed
8

Nicolas Hadacek's avatar
Nicolas Hadacek committed
9
Field::Field(QWidget *parent, const char *name)
10
: QFrame(parent, name), lev(LEVELS[0]), random(0), state(Stopped),
11
  u_mark(false), cursor(false), _reveal(false), _autoreveal(false)
Stephan Kulow's avatar
Stephan Kulow committed
12
{
Nicolas Hadacek's avatar
Nicolas Hadacek committed
13
14
15
	setFrameStyle( QFrame::Box | QFrame::Raised );
	setLineWidth(2);
	setMidLineWidth(2);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
16

Nicolas Hadacek's avatar
Nicolas Hadacek committed
17
18
19
20
21
22
23
24
	QVBoxLayout *top = new QVBoxLayout(this, 0);
	top->addStretch(1);
	pb = new QPushButton(i18n("Press to resume"), this);
	pb->hide();
	top->addWidget(pb, 0, AlignCenter);
	connect(pb, SIGNAL(clicked()), this, SLOT(resume()));
	top->addStretch(1);

Nicolas Hadacek's avatar
Nicolas Hadacek committed
25
26
27
	QFont f = font();
	f.setBold(true);
	setFont(f);
28
29
30
31
32
33
	
	readSettings();
}

void Field::readSettings()
{
34
	setCaseProperties(OptionDialog::readCaseProperties());
35
36
37
38
	setUMark(OptionDialog::readUMark());
	setCursor(OptionDialog::readKeyboard());
	for (uint i=0; i<3; i++)
		mb[i] = OptionDialog::readMouseBinding((MouseButton)i);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
39
40
}

41
void Field::setCaseProperties(const CaseProperties &_cp)
Nicolas Hadacek's avatar
Nicolas Hadacek committed
42
{
43
44
	cp = _cp;
	
Nicolas Hadacek's avatar
Nicolas Hadacek committed
45
46
	QBitmap mask;

47
48
	flagPixmap(mask, true);
	flagPixmap(pm_flag, false);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
49
50
	pm_flag.setMask(mask);

51
52
	minePixmap(mask, true, Covered);
	minePixmap(pm_mine, false, Covered);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
53
	pm_mine.setMask(mask);
54

55
56
	minePixmap(mask, true, Exploded);
	minePixmap(pm_exploded, false, Exploded);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
57
58
	pm_exploded.setMask(mask);

59
60
	minePixmap(mask, true, Marked);
	minePixmap(pm_error, false, Marked);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
61
62
	pm_error.setMask(mask);

63
64
	cursorPixmap(mask, true);
	cursorPixmap(pm_cursor, false);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
65
66
	pm_cursor.setMask(mask);

Nicolas Hadacek's avatar
Nicolas Hadacek committed
67
	QFont f = font();
68
	f.setPointSize(cp.size-6);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
69
	setFont(f);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
70
71
72
73
74
75

	updateGeometry();
}

void Field::flagPixmap(QPixmap &pix, bool mask) const
{
76
	pix.resize(cp.size, cp.size);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
77
78
79
80
	if (mask) pix.fill(color0);
	QPainter p(&pix);
	p.setWindow(0, 0, 16, 16);
	p.setPen( (mask ? color1 : black) );
Nicolas Hadacek's avatar
Nicolas Hadacek committed
81
82
83
84
	p.drawLine(6, 13, 14, 13);
	p.drawLine(8, 12, 12, 12);
	p.drawLine(9, 11, 11, 11);
	p.drawLine(10, 2, 10, 10);
85
86
	if (!mask) p.setPen(cp.flagColor);
	p.setBrush( (mask ? color1 : cp.flagColor) );
Nicolas Hadacek's avatar
Nicolas Hadacek committed
87
	p.drawRect(4, 3, 6, 5);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
88
}
Nicolas Hadacek's avatar
Nicolas Hadacek committed
89

Nicolas Hadacek's avatar
Nicolas Hadacek committed
90
91
void Field::cursorPixmap(QPixmap &pix, bool mask) const
{
92
	pix.resize(cp.size, cp.size);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
93
94
95
96
97
98
	if (mask) pix.fill(color0);
	QPainter p(&pix);
	p.setWindow(0, 0, 20, 20);
	p.setPen( (mask ? color1 : black) );
	p.drawRect(2, 2, 16, 16);
}
Nicolas Hadacek's avatar
Nicolas Hadacek committed
99

100
void Field::minePixmap(QPixmap &pix, bool mask, CaseState type) const
Nicolas Hadacek's avatar
Nicolas Hadacek committed
101
{
102
	pix.resize(cp.size, cp.size);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
103
104
105
106
	if (mask) pix.fill(color0);
	QPainter p(&pix);
	p.setWindow(0, 0, 20, 20);

107
108
	if ( type==Exploded )
		p.fillRect(2, 2, 16, 16, (mask ? color1 : cp.explosionColor));
Nicolas Hadacek's avatar
Nicolas Hadacek committed
109
110
111
112
113
114
115
116
117

	QPen pen(mask ? color1 : black, 1);
	p.setPen(pen);
	p.setBrush(mask ? color1 : black);
	p.drawLine(10,3,10,18);
	p.drawLine(3,10,18,10);
	p.drawLine(5, 5, 16, 16);
	p.drawLine(5, 15, 15, 5);
	p.drawEllipse(5, 5, 11, 11);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
118
	
Nicolas Hadacek's avatar
Nicolas Hadacek committed
119
120
	p.fillRect(8, 8, 2, 2, (mask ? color1 : white));

121
	if ( type==Marked ) {
Nicolas Hadacek's avatar
Nicolas Hadacek committed
122
		if (!mask) {
123
			pen.setColor(cp.errorColor);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
124
125
126
127
128
129
130
131
132
			p.setPen(pen);
		}
		p.drawLine(3, 3, 17, 17);
		p.drawLine(4, 3, 17, 16);
		p.drawLine(3, 4, 16, 17);
		p.drawLine(3, 17, 17, 3);
		p.drawLine(3, 16, 16, 3);
		p.drawLine(4, 17, 17, 4);
	}
Stephan Kulow's avatar
Stephan Kulow committed
133
134
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
135
QSize Field::sizeHint() const
Stephan Kulow's avatar
Stephan Kulow committed
136
{
137
138
	return QSize(2*frameWidth() + lev.width*cp.size,
				 2*frameWidth() + lev.height*cp.size);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
139
140
141
142
143
144
145
}

QSizePolicy Field::sizePolicy() const
{
	return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
}

146
147
148
149
150
151
const Case &Field::pfield(uint i, uint j) const
{
	return _pfield[i + j*(lev.width+2)];
}

Case &Field::pfield(uint i, uint j)
Stephan Kulow's avatar
Stephan Kulow committed
152
{
Nicolas Hadacek's avatar
Nicolas Hadacek committed
153
	return _pfield[i + j*(lev.width+2)];
Stephan Kulow's avatar
Stephan Kulow committed
154
155
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
156
uint Field::computeNeighbours(uint i, uint j) const
Stephan Kulow's avatar
Stephan Kulow committed
157
{
Nicolas Hadacek's avatar
Nicolas Hadacek committed
158
	uint nm = 0;
Stephan Kulow's avatar
Stephan Kulow committed
159
	
160
161
162
163
164
165
166
167
	if (pfield(i-1,   j).mine) nm++;
	if (pfield(i-1, j+1).mine) nm++;
	if (pfield(i-1, j-1).mine) nm++;
	if (pfield(  i, j+1).mine) nm++;
	if (pfield(  i, j-1).mine) nm++;
	if (pfield(i+1,   j).mine) nm++;
	if (pfield(i+1, j+1).mine) nm++;
	if (pfield(i+1, j-1).mine) nm++;
Stephan Kulow's avatar
Stephan Kulow committed
168
169
170
171
	
	return nm;
}

172
void Field::setLevel(const Level &l)
Stephan Kulow's avatar
Stephan Kulow committed
173
{
Nicolas Hadacek's avatar
Nicolas Hadacek committed
174
	lev = l;
175
	restart(false);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
176
177
	updateGeometry();
}
Stephan Kulow's avatar
Stephan Kulow committed
178

Nicolas Hadacek's avatar
Nicolas Hadacek committed
179
180
181
void Field::restart(bool repaint)
{
	/* if game is paused : resume before restart */
182
	if ( state==Paused ) {
Stephan Kulow's avatar
Stephan Kulow committed
183
184
185
		resume();
		emit freezeTimer();
	}
Nicolas Hadacek's avatar
Nicolas Hadacek committed
186

187
	state = Playing;
188
	first_click = true;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
189

Nicolas Hadacek's avatar
Nicolas Hadacek committed
190
	_pfield.resize( (lev.width+2) * (lev.height+2) );
Stephan Kulow's avatar
Stephan Kulow committed
191
	
Nicolas Hadacek's avatar
Nicolas Hadacek committed
192
193
	QPainter *p;
	if (repaint) p = new QPainter(this);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
194
195
	for (uint i=0; i<lev.width+2; i++)
		for (uint j=0; j<lev.height+2; j++) {
196
197
198
199
200
201
			Case tmp;
			tmp.mine  = false;
			tmp.state = (i==0 || i==lev.width+1 || j==0 || j==lev.height+1
						 ? Uncovered : Covered);
			if ( pfield(i, j).mine==false && pfield(i, j).state==tmp.state )
				continue;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
202
			pfield(i, j) = tmp;
203
			if (repaint && tmp.state==Covered) drawCase(i, j, p);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
204
		}
Nicolas Hadacek's avatar
Nicolas Hadacek committed
205

206
	if (repaint) drawCursor(false, p);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
207
208
	ic = lev.width/2;
	jc = lev.height/2;
209
	if ( repaint && cursor ) drawCursor(true, p);
Stephan Kulow's avatar
Stephan Kulow committed
210
211
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
212
void Field::paintEvent(QPaintEvent *e)
Stephan Kulow's avatar
Stephan Kulow committed
213
{
214
	if ( state==Paused ) return;		
Nicolas Hadacek's avatar
Nicolas Hadacek committed
215

Nicolas Hadacek's avatar
Nicolas Hadacek committed
216
217
	QPainter p(this);
	drawFrame(&p);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
218
219
220
221
222
	uint imin = (uint)QMAX(QMIN(xToI(e->rect().left()), (int)lev.width), 1);
	uint imax = (uint)QMAX(QMIN(xToI(e->rect().right()), (int)lev.width), 1);
	uint jmin = (uint)QMAX(QMIN(yToJ(e->rect().top()), (int)lev.height), 1);
	uint jmax = (uint)QMAX(QMIN(yToJ(e->rect().bottom()), (int)lev.height), 1);
	for (uint i=imin; i<=imax; i++)
Nicolas Hadacek's avatar
Nicolas Hadacek committed
223
	    for (uint j=jmin; j<=jmax; j++) drawCase(i, j, &p);
Stephan Kulow's avatar
Stephan Kulow committed
224
225
}

226
void Field::changeCaseState(uint i, uint j, CaseState new_st)
Stephan Kulow's avatar
Stephan Kulow committed
227
{
228
229
	emit changeCase(pfield(i, j).state, -1);
	pfield(i, j).state = new_st;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
230
	emit changeCase(new_st, 1);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
231
	drawCase(i, j);
232
	if ( state==Playing ) emit updateStatus(pfield(i, j).mine);
Stephan Kulow's avatar
Stephan Kulow committed
233
234
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
235
236
int Field::iToX(uint i) const
{
237
	return (i-1)*cp.size + frameWidth();
Nicolas Hadacek's avatar
Nicolas Hadacek committed
238
}
Stephan Kulow's avatar
Stephan Kulow committed
239

Nicolas Hadacek's avatar
Nicolas Hadacek committed
240
int Field::jToY(uint j) const
Stephan Kulow's avatar
Stephan Kulow committed
241
{
242
	return (j-1)*cp.size + frameWidth();
Nicolas Hadacek's avatar
Nicolas Hadacek committed
243
244
245
246
}

int Field::xToI(int x) const
{
Stephan Kulow's avatar
Stephan Kulow committed
247
	// the cast is necessary when x-frameWidth() is negative ( ?? )
248
	return (int)((double)(x - frameWidth())/cp.size) + 1;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
249
250
251
252
}

int Field::yToJ(int y) const
{
253
	return (int)((double)(y - frameWidth())/cp.size) + 1;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
254
255
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
256
void Field::uncover(uint i, uint j)
Stephan Kulow's avatar
Stephan Kulow committed
257
{
258
	if ( pfield(i, j).state!=Covered ) return;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
259
	uint nbs = computeNeighbours(i, j);
260

Stephan Kulow's avatar
Stephan Kulow committed
261
	if (!nbs) {
262
		changeCaseState(i, j, Uncovered);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
263
264
265
266
267
268
269
270
		uncover(i-1, j+1);
		uncover(i-1,   j);
		uncover(i-1, j-1);
		uncover(  i, j+1);
		uncover(  i, j-1);
		uncover(i+1, j+1);
		uncover(i+1,   j);
		uncover(i+1, j-1);
271
	} else changeCaseState(i, j, Uncovered);
Stephan Kulow's avatar
Stephan Kulow committed
272
273
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
274
bool Field::inside(int i, int j) const
Nicolas Hadacek's avatar
Nicolas Hadacek committed
275
{
Nicolas Hadacek's avatar
Nicolas Hadacek committed
276
	return ( i>=1 && i<=(int)lev.width && j>=1 && j<=(int)lev.height);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
277
278
}

279
280
281
282
283
284
285
286
287
288
MouseAction Field::mapMouseButton(QMouseEvent *e) const
{
	switch (e->button()) {
	case LeftButton:  return mb[Left];
	case MidButton:   return mb[Mid];
	case RightButton: return mb[Right];
	default:          return Mark;
	}
}

289
void Field::mousePressEvent(QMouseEvent *e)
Stephan Kulow's avatar
Stephan Kulow committed
290
{
291
	if ( state!=Playing ) return;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
292
	setMood(Smiley::Stressed);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
293
	bool inside = placeCursor(xToI(e->pos().x()), yToJ(e->pos().y()));
Stephan Kulow's avatar
Stephan Kulow committed
294

295
296
	switch ( mapMouseButton(e) ) {
	case Reveal:
297
298
		_reveal = true;
		if (inside) pressCase(ic, jc, false);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
299
		break;
300
	case Mark:
Nicolas Hadacek's avatar
Nicolas Hadacek committed
301
302
		if (inside) mark();
		break;
303
304
305
306
	case UMark:
		if (inside) umark();
		break;
	case AutoReveal:
307
308
		_autoreveal = true;
		if (inside) pressClearFunction(ic, jc, false);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
309
		break;
Stephan Kulow's avatar
Stephan Kulow committed
310
311
312
	}
}

313
void Field::mouseReleaseEvent(QMouseEvent *e)
Stephan Kulow's avatar
Stephan Kulow committed
314
{
315
	if ( state!=Playing ) return;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
316
	setMood(Smiley::Normal);
Stephan Kulow's avatar
Stephan Kulow committed
317

Nicolas Hadacek's avatar
Nicolas Hadacek committed
318
	if ( inside(ic, jc) )
319
320
321
		if (_autoreveal) pressClearFunction(ic, jc, true);
	_reveal = false;
	_autoreveal = false;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
322
323
	if ( !placeCursor(xToI(e->pos().x()), yToJ(e->pos().y())) ) return;

324
325
	switch ( mapMouseButton(e) ) {
	case Reveal:
326
		reveal();
Nicolas Hadacek's avatar
Nicolas Hadacek committed
327
		break;
328
329
330
331
	case Mark:
	case UMark:
		break;
	case AutoReveal:
332
		autoReveal();
Nicolas Hadacek's avatar
Nicolas Hadacek committed
333
		break;
Stephan Kulow's avatar
Stephan Kulow committed
334
335
336
	}
}

337
void Field::mouseMoveEvent(QMouseEvent *e)
Stephan Kulow's avatar
Stephan Kulow committed
338
{
339
	if ( state!=Playing ) return;
Stephan Kulow's avatar
Stephan Kulow committed
340

Nicolas Hadacek's avatar
Nicolas Hadacek committed
341
	if ( inside(ic, jc) ) {
342
343
		if (_reveal) pressCase(ic, jc, true);
		if (_autoreveal) pressClearFunction(ic, jc, true);
Stephan Kulow's avatar
Stephan Kulow committed
344
345
	}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
346
347
	if ( !placeCursor(xToI(e->pos().x()), yToJ(e->pos().y())) ) return;

348
349
	if (_reveal) pressCase(ic, jc, false);
	else if (_autoreveal) pressClearFunction(ic, jc, false);
Stephan Kulow's avatar
Stephan Kulow committed
350
351
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
352
void Field::showMines()
Stephan Kulow's avatar
Stephan Kulow committed
353
{
Nicolas Hadacek's avatar
Nicolas Hadacek committed
354
	for(uint i=1; i<=lev.width; i++)
355
356
357
358
359
		for(uint j=1; j<=lev.height; j++) {
		    if ( !pfield(i, j).mine ) continue;
			if ( pfield(i, j).state!=Exploded && pfield(i, j).state!=Marked )
				changeCaseState(i, j, Uncovered);
		}
Stephan Kulow's avatar
Stephan Kulow committed
360
361
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
362
void Field::pressCase(uint i, uint j, bool pressed, QPainter *p)
Stephan Kulow's avatar
Stephan Kulow committed
363
{
364
	if ( pfield(i, j).state==Covered ) drawBox(iToX(i), jToY(j), pressed, p);
Stephan Kulow's avatar
Stephan Kulow committed
365
366
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
367
void Field::pressClearFunction(uint i, uint j, bool pressed)
Stephan Kulow's avatar
Stephan Kulow committed
368
{
Nicolas Hadacek's avatar
Nicolas Hadacek committed
369
370
371
372
373
374
375
376
377
378
	QPainter p(this);
	pressCase(i-1, j+1, pressed, &p);
	pressCase(i-1,   j, pressed, &p);
	pressCase(i-1, j-1, pressed, &p);
	pressCase(  i, j+1, pressed, &p);
	pressCase(  i,   j, pressed, &p);
	pressCase(  i, j-1, pressed, &p);
	pressCase(i+1, j-1, pressed, &p);
	pressCase(i+1,   j, pressed, &p);
	pressCase(i+1, j+1, pressed, &p);
Stephan Kulow's avatar
Stephan Kulow committed
379
380
}

381
382
#define M_OR_U(i, j) ( pfield(i, j).state==Marked \
					   || pfield(i, j).state==Uncertain )
Nicolas Hadacek's avatar
Nicolas Hadacek committed
383

384
385
void Field::keyboardAutoReveal()
{
386
	pressClearFunction(ic, jc, false);
387
388
389
390
391
	QTimer::singleShot(50, this, SLOT(keyboardAutoRevealSlot()));
}

void Field::keyboardAutoRevealSlot()
{
392
393
	pressClearFunction(ic, jc, true);
	drawCursor(true);
394
395
396
	autoReveal();
}

397
void Field::autoReveal()
Stephan Kulow's avatar
Stephan Kulow committed
398
{
399
	if ( state!=Playing ) return;
400
401
402
403
404
405
	switch (pfield(ic, jc).state) {
	case Covered:
	case Marked:
	case Uncertain: return;
	default:        break;
	}
Stephan Kulow's avatar
Stephan Kulow committed
406
407
	
	/* number of mines around the case */
Nicolas Hadacek's avatar
Nicolas Hadacek committed
408
	uint nm = computeNeighbours(ic, jc);
409
410
411
412
413
414
415
416
	if M_OR_U(ic-1,   jc) nm--;
	if M_OR_U(ic-1, jc+1) nm--;
	if M_OR_U(ic-1, jc-1) nm--;
	if M_OR_U(  ic, jc+1) nm--;
	if M_OR_U(  ic, jc-1) nm--;
	if M_OR_U(ic+1,   jc) nm--;
	if M_OR_U(ic+1, jc+1) nm--;
	if M_OR_U(ic+1, jc-1) nm--;
Stephan Kulow's avatar
Stephan Kulow committed
417
418
419
	
	if (!nm) { /* the number of surrounding mines is equal */
		       /* to the number of marks :) */
420
421
422
423
424
425
426
427
		uncoverCase(ic+1, jc+1);
		uncoverCase(ic+1,   jc);
		uncoverCase(ic+1, jc-1);
		uncoverCase(  ic, jc+1);
		uncoverCase(  ic, jc-1);
		uncoverCase(ic-1, jc+1);
		uncoverCase(ic-1,   jc);
		uncoverCase(ic-1, jc-1);
Stephan Kulow's avatar
Stephan Kulow committed
428
429
	}
}
430
	
Nicolas Hadacek's avatar
Nicolas Hadacek committed
431
void Field::uncoverCase(uint i, uint j)
Stephan Kulow's avatar
Stephan Kulow committed
432
{
433
434
	if ( pfield(i, j).state==Covered ) {
		if ( pfield(i, j).mine ) changeCaseState(i, j, Uncovered);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
435
		else uncover(i, j);
Stephan Kulow's avatar
Stephan Kulow committed
436
	}
437

Stephan Kulow's avatar
Stephan Kulow committed
438
	/* to enable multiple explosions ... */
439
440
441
	if ( pfield(i, j).mine && pfield(i, j).state==Uncovered ) {
		changeCaseState(i, j, Exploded);
		_endGame();
Stephan Kulow's avatar
Stephan Kulow committed
442
443
444
	}
}

445
446
447
448
449
450
451
452
453
454
455
void Field::_endGame()
{
	if ( state==Stopped ) return;
	/* find all errors */
	for (uint ii=1; ii<lev.width+1; ii++)
		for (uint jj=1; jj<lev.width+1; jj++)
			if ( pfield(ii, jj).state==Marked && !pfield(ii, jj).mine )
				changeCaseState(ii, jj, Error);
	emit endGame();
}

Stephan Kulow's avatar
Stephan Kulow committed
456
457
void Field::pause()
{
458
	if (first_click) return;
Stephan Kulow's avatar
Stephan Kulow committed
459
460
	
	/* if already in pause : resume game */
461
	if ( state==Paused ) resume();
Nicolas Hadacek's avatar
Nicolas Hadacek committed
462
463
	else {
		emit freezeTimer();
Nicolas Hadacek's avatar
Nicolas Hadacek committed
464
		eraseField();
Nicolas Hadacek's avatar
Nicolas Hadacek committed
465
466
		pb->show();
		pb->setFocus();
467
468
		state = Paused;
		emit gameStateChanged(Paused);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
469
	}
Stephan Kulow's avatar
Stephan Kulow committed
470
471
472
473
}

void Field::resume()
{
474
475
	state = Playing;
	emit gameStateChanged(Playing);
Stephan Kulow's avatar
Stephan Kulow committed
476
	pb->hide();
Nicolas Hadacek's avatar
Nicolas Hadacek committed
477
478
	emit startTimer();
	update();
Stephan Kulow's avatar
Stephan Kulow committed
479
}
480
481
482

void Field::up()
{
483
	placeCursor(ic, jc-1, true);
484
485
486
487
}

void Field::down()
{
488
	placeCursor(ic, jc+1, true);
489
490
491
492
}

void Field::left()
{
493
	placeCursor(ic-1, jc, true);
494
495
496
497
}

void Field::right()
{
498
	placeCursor(ic+1, jc, true);
499
500
501
502
}

void Field::reveal()
{
503
	if ( state!=Playing ) return;
504
	if ( first_click ) {
505
		// set mines positions on field ; must avoid the first
506
507
508
509
510
511
		// clicked case
		for(uint k=0; k<lev.nbMines; k++) {
			uint i, j;
			do {
				i = random.getLong(lev.width);
				j = random.getLong(lev.height);
512
513
			} while ( pfield(i+1, j+1).mine
					  || ((i+1)==(uint)ic && (j+1)==(uint)jc) );
514
			
515
			pfield(i+1, j+1).mine = true;
516
517
		}
		emit startTimer();
518
		first_click = false;
519
		emit gameStateChanged(Playing);
520
521
522
523
524
525
526
	}
	
	uncoverCase(ic, jc);
}

void Field::mark()
{
527
	if ( state!=Playing ) return;
528
529
530
531
532
533
534
	switch (pfield(ic, jc).state) {
	case Covered:   changeCaseState(ic, jc, Marked); break;
	case Marked:    changeCaseState(ic, jc, (u_mark ? Uncertain : Covered));
		            break;
	case Uncertain:	changeCaseState(ic, jc, Covered); break;
	default:        break;
	}
Nicolas Hadacek's avatar
Nicolas Hadacek committed
535
}
536

537
538
539
void Field::umark()
{
	if ( state!=Playing ) return;
540
541
542
543
544
545
	switch (pfield(ic, jc).state) {
	case Covered:
	case Marked:    changeCaseState(ic, jc, Uncertain); break;
	case Uncertain: changeCaseState(ic, jc, Covered); break;
	default:        break;
	}
546
547
}

Nicolas Hadacek's avatar
Nicolas Hadacek committed
548
549
bool Field::placeCursor(int i, int j, bool check)
{
550
551
	if ( check && (state!=Playing || !inside(i, j)) ) return false;
	if ( cursor && inside(ic, jc) ) drawCursor(false);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
552
553
	ic = i;
	jc = j;
554
555
556
	if ( !inside(i, j) ) return false;
	if (cursor) drawCursor(true);
	return true;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
557
558
559
560
}

void Field::setCursor(bool show)
{
561
	if ( state==Playing && inside(ic, jc) ) {
562
563
		if ( cursor && !show ) drawCursor(false);
		if ( !cursor && show) drawCursor(true);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
564
565
	}
	cursor = show;
566
}
Nicolas Hadacek's avatar
Nicolas Hadacek committed
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581

// draw methods
QPainter *Field::begin(QPainter *pt)
{
	return (pt ? pt : new QPainter(this));
}

void Field::end(QPainter *p, const QPainter *pt)
{
	if (pt==0) delete p;
}

void Field::drawBox(int x, int y, bool pressed, QPainter *pt)
{
	QPainter *p = begin(pt);
582
583
	p->eraseRect(x, y, cp.size, cp.size);
	qDrawWinPanel(p, x, y, cp.size, cp.size, colorGroup(), !pressed);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
584
585
586
587
588
589
590
591
	end(p, pt);
}

void Field::drawCase(uint i, uint j, QPainter *pt)
{
	QPainter *p = begin(pt);
	int x = iToX(i);
	int y = jToY(j);
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620

	switch (pfield(i, j).state) {
	case Covered:   drawBox(x, y, true, p);
		            break;
	case Marked:    drawBox(x, y, true, p);
		            p->drawPixmap(x, y, pm_flag);
		            break;
	case Error:     drawBox(x, y, true, p);
					p->drawPixmap(x, y, pm_error);
		            break;
	case Uncertain: drawBox(x, y, true, p);
		            p->setPen(black);
					p->drawText(x, y, cp.size, cp.size, AlignCenter, "?");
					break;
	case Exploded:  drawBox(x, y, false, p);
		            p->drawPixmap(x, y, pm_exploded);
					break;
	case Uncovered: drawBox(x, y, false, p);
		            if ( pfield(i, j).mine ) p->drawPixmap(x, y, pm_mine);
					else {
						uint n = computeNeighbours(i, j);
						if (n) {
							char nb[2] = "0";
							nb[0] += n;
							p->setPen(cp.numberColors[n-1]);
							p->drawText(x, y, cp.size, cp.size,
										AlignCenter, nb);
						}
					}
Nicolas Hadacek's avatar
Nicolas Hadacek committed
621
622
	}

623
	if ( cursor && (int)i==ic && (int)j==jc ) drawCursor(true, p);
Nicolas Hadacek's avatar
Nicolas Hadacek committed
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
	end(p, pt);
}

void Field::eraseField()
{
	QPainter p(this);
	p.eraseRect(0, 0, width(), height());
}

void Field::drawCursor(bool show, QPainter *pt)
{
	QPainter *p = begin(pt);
	if (show) p->drawPixmap(iToX(ic), jToY(jc), pm_cursor);
	else {
		bool b = cursor;
639
		if (b) cursor = false;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
640
		drawCase(ic, jc, p);
641
		if (b) cursor = true;
Nicolas Hadacek's avatar
Nicolas Hadacek committed
642
643
644
	}
	end(p, pt);
}