Commit ff73bac6 authored by Nicolas Hadacek's avatar Nicolas Hadacek
Browse files

lot's of little things (see CHANGELOG)

svn path=/trunk/kdegames/kmines/; revision=45812
parent 7e671d99
2.0.3
* use kkeydialog for actions
* rationalize settings (much better now)
* animate autoreveal with keyboard
* enable/disable and change text for pause in menu
* configuration of mouse bindings [idea of FX Duranceau]
2.0.2
* XMLify the GUI
......@@ -20,7 +27,7 @@
1.0.6
* slightly better highscores dialog
* fixed a bug in flagged mines display (cannot be negative)
* fixed a bug in flagged mines display (cannot be negative)
* the LCDs gets red when there are more flagged cases than mines
and when you have used more time than the better player.
* use of a status bar.
......@@ -42,7 +49,7 @@
* level menu items are now checked
1.0.2
* bug fix : argh! a "brown paper bag" one (bug report by Szokovacs Robert)
* bug fix : argh! a "brown paper bag" one (bug report by Szokovacs Robert)
1.0.1
* bug fix : when paused a game can be continued by changing desktops or
......@@ -70,7 +77,7 @@
* bugfix from Anders Widell (doesn't allow anymore to middle click on a flag)
0.6.8
* some polishing (game over message do not overwrite mines number & marked
* some polishing (game over message do not overwrite mines number & marked
mines stay marked even when the game is lost : so you can completely analyse
why you have lost :) -- thanx to Christoph Rummel for pointing these to me.
......@@ -90,12 +97,12 @@
* adapted to libkdecore 0.7
* hide/show menubar and popup
* more compliant to the Style Guide
0.6.3
* use kdehelp
* updating of Makefile (use KDEDIR)
* use kmsgbox
0.62
* use KKeyCode to manage some keyboard shortcuts
......
- better case scaling can be obtained with QPAinter::scale(double) ??
- layout bug when changing level/case size (fvwm specific ?)
- configuration for mouse buttons' actions
- disable "pause game" when game stopped
+ change to "resume game" when game paused
IDEAS:
* do you have any idea ?
KNOWN BUGS:
* layout bug when changing level/case size/menubar visible
[fvwm specific ? -> no !]
......@@ -6,6 +6,8 @@ const char *OP_MENUBAR = "menubar visible";
const char *OP_LEVEL = "Level";
const char *OP_CASE_SIZE = "case size";
const char *OP_KEYBOARD = "keyboard game";
const char *OP_MOUSE_BINDINGS[3]
= { "mouse left", "mouse mid", "mouse right" };
const char *HS_NAME = "Name";
const char *HS_MIN = "Min";
......@@ -17,7 +19,7 @@ const uint MIN_CASE_SIZE = 20;
const uint MAX_CASE_SIZE = 100;
const Level LEVELS[NbLevels-1] = {
{8, 8, 10}, // Easy
{16, 16, 40}, // Normal
{30, 16, 99} // Expert
{8, 8, 10, Easy },
{16, 16, 40, Normal},
{30, 16, 99, Expert}
};
......@@ -10,6 +10,7 @@ extern const char *OP_MENUBAR;
extern const char *OP_LEVEL;
extern const char *OP_CASE_SIZE;
extern const char *OP_KEYBOARD;
extern const char *OP_MOUSE_BINDINGS[3];
extern const char *HS_NAME;
extern const char *HS_MIN;
......@@ -23,19 +24,28 @@ extern const char *HS_SEC;
#define MARKED 8
#define UNCOVERED 16
#define EXPLODED 32
#define ERROR 64
#define ERROR 64
/* Default case size */
extern const uint CASE_SIZE;
extern const uint MIN_CASE_SIZE;
extern const uint MAX_CASE_SIZE;
/* Predefined levels */
enum GameType { Easy = 0, Normal, Expert, Custom, NbLevels };
typedef struct { uint width, height, nbMines; } Level;
enum GameType { Easy = 0, Normal, Expert, Custom, NbLevels };
enum GameState { Stopped, Playing, Paused, GameOver };
enum MouseAction { Reveal = 0, Mark, AutoReveal, UMark };
enum MouseButton { Left = 0, Mid, Right };
struct Level {
uint width, height, nbMines;
GameType type;
};
extern const Level LEVELS[NbLevels-1];
extern const char *HS_GRP[NbLevels-1];
typedef struct { uint sec, min, mode; } Score;
struct Score {
uint sec, min;
GameType type;
};
#endif // DEFINES_H
......@@ -4,10 +4,11 @@
#include <qpixmap.h>
#include <qpushbutton.h>
#include <qfont.h>
#include <qvgroupbox.h>
#include <kapp.h>
#include <klocale.h>
#include <kconfig.h>
#include <knuminput.h>
#include "bitmaps/smile"
#include "bitmaps/smile_happy"
#include "bitmaps/smile_ohno"
......@@ -216,14 +217,14 @@ void CustomDialog::widthChanged(int n)
emit setWidth(n);
nbMinesChanged(lev->nbMines);
}
void CustomDialog::heightChanged(int n)
{
lev->height = (uint)n;
emit setHeight(n);
nbMinesChanged(lev->nbMines);
}
void CustomDialog::nbMinesChanged(int n)
{
lev->nbMines = (uint)n;
......@@ -252,8 +253,8 @@ WHighScores::WHighScores(QWidget *parent, const Score *score)
KConfig *conf = kapp->config();
if (score) { // set highscores
mode = score->mode;
conf->setGroup(HS_GRP[mode]);
type = score->type;
conf->setGroup(HS_GRP[type]);
conf->writeEntry(HS_NAME, i18n("Anonymous")); // default
conf->writeEntry(HS_MIN, score->min);
conf->writeEntry(HS_SEC, score->sec);
......@@ -269,7 +270,7 @@ WHighScores::WHighScores(QWidget *parent, const Score *score)
/* level names */
QString str;
for(uint k=0; k<3; k++) {
for(int k=0; k<3; k++) {
if ( k==0 ) str = i18n("Easy");
else if ( k==1 ) str = i18n("Normal");
else str = i18n("Expert");
......@@ -286,7 +287,7 @@ WHighScores::WHighScores(QWidget *parent, const Score *score)
int sec = conf->readNumEntry(HS_SEC, 0);
bool no_score = FALSE;
if ( !score || (k!=mode) ) {
if ( !score || (k!=type) ) {
lab = new QLabel(plainPage());
lab->setFont(f);
QString name = conf->readEntry(HS_NAME, "");
......@@ -331,7 +332,7 @@ WHighScores::WHighScores(QWidget *parent, const Score *score)
void WHighScores::writeName()
{
KConfig *conf = kapp->config();
conf->setGroup(HS_GRP[mode]);
conf->setGroup(HS_GRP[type]);
QString str = qle->text();
if ( str.length() ) conf->writeEntry(HS_NAME, str);
conf->sync();
......@@ -350,35 +351,104 @@ void WHighScores::reject()
}
//-----------------------------------------------------------------------------
OptionDialog::OptionDialog(uint &caseSize, QWidget *parent)
: DialogBase(i18n("Game settings"), Ok|Cancel, Cancel, parent),
cs(caseSize)
OptionDialog::OptionDialog(QWidget *parent)
: DialogBase(i18n("Settings"), Ok|Cancel, Cancel, parent)
{
KIntNumInput *ni = new KIntNumInput(0, caseSize, plainPage(), 10);
ni = new KIntNumInput(0, readCaseSize(), plainPage(), 10);
ni->setRange(MIN_CASE_SIZE, MAX_CASE_SIZE, 1, true);
ni->setLabel(i18n("Case size"));
top->addWidget(ni);
connect(ni, SIGNAL(valueChanged(int)), SLOT(changed(int)));
top->addSpacing(spacingHint());
um = new QCheckBox(i18n("Enable ? mark"), plainPage());
um->setChecked(readUMark());
top->addWidget(um);
keyb = new QCheckBox(i18n("Enable keyboard"), plainPage());
keyb->setChecked(readKeyboard());
top->addWidget(keyb);
top->addSpacing(spacingHint());
QVGroupBox *gb = new QVGroupBox(i18n("Mouse bindings"), plainPage());
top->addWidget(gb);
QGrid *grid = new QGrid(2, gb);
grid->setSpacing(spacingHint());
QLabel *lab = new QLabel(i18n("Left button"), grid);
cb[Left] = new QComboBox(FALSE, grid);
lab = new QLabel(i18n("Mid button"), grid);
cb[Mid] = new QComboBox(FALSE, grid);
lab = new QLabel(i18n("Right button"), grid);
cb[Right] = new QComboBox(FALSE, grid);
for (uint i=0; i<3; i++) {
cb[i]->insertItem(i18n("reveal"), 0);
cb[i]->insertItem(i18n("toggle mark"), 1);
cb[i]->insertItem(i18n("autoreveal"), 2);
cb[i]->insertItem(i18n("toggle ? mark"), 3);
cb[i]->setCurrentItem(readMouseBinding((MouseButton)i));
}
}
void OptionDialog::changed(int nb)
KConfig *OptionDialog::config()
{
cs = nb;
KConfig *conf = kapp->config();
conf->setGroup(OP_GRP);
return conf;
}
void OptionDialog::accept()
{
KConfig *conf = kapp->config();
conf->setGroup(OP_GRP);
conf->writeEntry(OP_CASE_SIZE, cs);
KConfig *conf = config();
conf->writeEntry(OP_CASE_SIZE, ni->value());
conf->writeEntry(OP_UMARK, um->isChecked());
conf->writeEntry(OP_KEYBOARD, keyb->isChecked());
for (uint i=0; i<3; i++)
conf->writeEntry(OP_MOUSE_BINDINGS[i], cb[i]->currentItem());
DialogBase::accept();
}
uint OptionDialog::caseSize()
uint OptionDialog::readCaseSize()
{
KConfig *conf = kapp->config();
conf->setGroup(OP_GRP);
uint cs = conf->readUnsignedNumEntry(OP_CASE_SIZE, CASE_SIZE);
cs = QMAX(QMIN(cs, MAX_CASE_SIZE), MIN_CASE_SIZE);
return cs;
uint cs = config()->readUnsignedNumEntry(OP_CASE_SIZE, CASE_SIZE);
return QMAX(QMIN(cs, MAX_CASE_SIZE), MIN_CASE_SIZE);
}
bool OptionDialog::readUMark()
{
return config()->readBoolEntry(OP_UMARK, TRUE);
}
bool OptionDialog::readKeyboard()
{
return config()->readBoolEntry(OP_KEYBOARD, TRUE);
}
GameType OptionDialog::readLevel()
{
GameType lev = (GameType)config()->readUnsignedNumEntry(OP_LEVEL, 0);
return lev>=Custom ? Easy : lev;
}
void OptionDialog::writeLevel(GameType lev)
{
if ( lev>=Custom ) return;
config()->writeEntry(OP_LEVEL, (uint)lev);
}
bool OptionDialog::readMenuVisible()
{
return config()->readBoolEntry(OP_MENUBAR, TRUE);
}
void OptionDialog::writeMenuVisible(bool visible)
{
config()->writeEntry(OP_MENUBAR, visible);
}
MouseAction OptionDialog::readMouseBinding(MouseButton mb)
{
MouseAction ma = (MouseAction)config()
->readUnsignedNumEntry(OP_MOUSE_BINDINGS[mb], mb);
return ma>UMark ? Reveal : ma;
}
......@@ -6,8 +6,12 @@
#include <qlineedit.h>
#include <qlcdnumber.h>
#include <qlayout.h>
#include <qcheckbox.h>
#include <qcombobox.h>
#include <kdialogbase.h>
#include <knuminput.h>
#include <kconfig.h>
#include "defines.h"
......@@ -79,7 +83,7 @@ class DialogBase : public KDialogBase
DialogBase(const QString &caption, int buttonMask,
ButtonCode defaultButton,
QWidget *parent, const char *name = 0);
protected:
QVBoxLayout *top;
};
......@@ -88,10 +92,10 @@ class DialogBase : public KDialogBase
class CustomDialog : public DialogBase
{
Q_OBJECT
public:
CustomDialog(Level &lev, QWidget *parent);
private slots:
void widthChanged(int);
void heightChanged(int);
......@@ -101,7 +105,7 @@ class CustomDialog : public DialogBase
void setWidth(int);
void setHeight(int);
void setNbMines(const QString &);
private:
QScrollBar *sm;
Level *lev;
......@@ -111,7 +115,7 @@ class CustomDialog : public DialogBase
class WHighScores : public DialogBase
{
Q_OBJECT
public:
WHighScores(QWidget *parent, const Score *score = 0);
static uint time(GameType);
......@@ -119,9 +123,9 @@ class WHighScores : public DialogBase
private slots:
void writeName();
void reject();
private:
uint mode;
GameType type;
QLineEdit *qle;
};
......@@ -131,15 +135,26 @@ class OptionDialog : public DialogBase
Q_OBJECT
public:
OptionDialog(uint &caseSize, QWidget *parent);
static uint caseSize();
OptionDialog(QWidget *parent);
static uint readCaseSize();
static bool readUMark();
static bool readKeyboard();
static GameType readLevel();
static void writeLevel(GameType);
static bool readMenuVisible();
static void writeMenuVisible(bool visible);
static MouseAction readMouseBinding(MouseButton);
private slots:
void changed(int);
void accept();
private:
uint &cs;
KIntNumInput *ni;
QCheckBox *um, *keyb;
QComboBox *cb[3];
static KConfig *config();
};
#endif // DIALOGS_H
......@@ -7,9 +7,8 @@
#include <klocale.h>
Field::Field(QWidget *parent, const char *name)
: QFrame(parent, name), lev(LEVELS[0]), random(0),
paused(FALSE), stopped(FALSE), u_mark(FALSE), cursor(FALSE),
left_down(FALSE), mid_down(FALSE)
: QFrame(parent, name), lev(LEVELS[0]), random(0), state(Stopped),
u_mark(FALSE), cursor(FALSE), _reveal(FALSE), _autoreveal(FALSE)
{
setFrameStyle( QFrame::Box | QFrame::Raised );
setLineWidth(2);
......@@ -24,6 +23,17 @@ Field::Field(QWidget *parent, const char *name)
top->addStretch(1);
setFont( QFont("Helvetica", 14, QFont::Bold) );
readSettings();
}
void Field::readSettings()
{
setCaseSize(OptionDialog::readCaseSize());
setUMark(OptionDialog::readUMark());
setCursor(OptionDialog::readKeyboard());
for (uint i=0; i<3; i++)
mb[i] = OptionDialog::readMouseBinding((MouseButton)i);
}
void Field::setCaseSize(uint cs)
......@@ -39,7 +49,7 @@ void Field::setCaseSize(uint cs)
minePixmap(mask, TRUE, MINE);
minePixmap(pm_mine, FALSE, MINE);
pm_mine.setMask(mask);
minePixmap(mask, TRUE, EXPLODED);
minePixmap(pm_exploded, FALSE, EXPLODED);
pm_exploded.setMask(mask);
......@@ -151,7 +161,7 @@ uint Field::computeNeighbours(uint i, uint j) const
return nm;
}
void Field::start(const Level &l)
void Field::setLevel(const Level &l)
{
lev = l;
restart(FALSE);
......@@ -161,12 +171,12 @@ void Field::start(const Level &l)
void Field::restart(bool repaint)
{
/* if game is paused : resume before restart */
if ( paused ) {
if ( state==Paused ) {
resume();
emit freezeTimer();
}
stopped = FALSE;
state = Playing;
first_click = TRUE;
_pfield.resize( (lev.width+2) * (lev.height+2) );
......@@ -190,7 +200,7 @@ void Field::restart(bool repaint)
void Field::paintEvent(QPaintEvent *e)
{
if (paused) return;
if ( state==Paused ) return;
QPainter p(this);
drawFrame(&p);
......@@ -211,10 +221,10 @@ void Field::changeCaseState(uint i, uint j, uint new_st)
emit changeCase(pfield(i, j), -1);
pfield(i, j) = new_st;
}
emit changeCase(new_st, 1);
drawCase(i, j);
if (!stopped) emit updateStatus(pfield(i, j) & MINE);
if ( state==Playing ) emit updateStatus(pfield(i, j) & MINE);
}
int Field::iToX(uint i) const
......@@ -242,7 +252,7 @@ void Field::uncover(uint i, uint j)
{
if ( !(pfield(i, j) & COVERED) ) return;
uint nbs = computeNeighbours(i, j);
if (!nbs) {
changeCaseState(i,j,UNCOVERED);
uncover(i-1, j+1);
......@@ -261,63 +271,77 @@ bool Field::inside(int i, int j) const
return ( i>=1 && i<=(int)lev.width && j>=1 && j<=(int)lev.height);
}
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;
}
}
void Field::mousePressEvent(QMouseEvent *e)
{
if ( locked() ) return;
if ( state!=Playing ) return;
setMood(Smiley::Stressed);
bool inside = placeCursor(xToI(e->pos().x()), yToJ(e->pos().y()));
switch (e->button()) {
case LeftButton:
left_down = TRUE;
switch ( mapMouseButton(e) ) {
case Reveal:
_reveal = TRUE;
if (inside) pressCase(ic, jc, FALSE);
break;
case RightButton:
case Mark:
if (inside) mark();
break;
case MidButton:
mid_down = TRUE;
case UMark:
if (inside) umark();
break;
case AutoReveal:
_autoreveal = TRUE;
if (inside) pressClearFunction(ic, jc, FALSE);
break;
default: break;
}
}
void Field::mouseReleaseEvent(QMouseEvent *e)
{
if ( locked() ) return;
if ( state!=Playing ) return;
setMood(Smiley::Normal);
if ( inside(ic, jc) )
if (mid_down) pressClearFunction(ic, jc, TRUE);
left_down = FALSE;
mid_down = FALSE;
if (_autoreveal) pressClearFunction(ic, jc, TRUE);
_reveal = FALSE;
_autoreveal = FALSE;
if ( !placeCursor(xToI(e->pos().x()), yToJ(e->pos().y())) ) return;
switch (e->button()) {
case LeftButton:
switch ( mapMouseButton(e) ) {
case Reveal:
reveal();
break;
case MidButton:
case Mark:
case UMark:
break;
case AutoReveal:
autoReveal();
break;
default: break;
}
}
void Field::mouseMoveEvent(QMouseEvent *e)
{
if ( locked() ) return;
if ( state!=Playing ) return;
if ( inside(ic, jc) ) {
if (left_down) pressCase(ic, jc, TRUE);
if (mid_down) pressClearFunction(ic, jc, TRUE);
if (_reveal) pressCase(ic, jc, TRUE);
if (_autoreveal) pressClearFunction(ic, jc, TRUE);
}
if ( !placeCursor(xToI(e->pos().x()), yToJ(e->pos().y())) ) return;
if (left_down) pressCase(ic, jc, FALSE);
else if (mid_down) pressClearFunction(ic, jc, FALSE);
if (_reveal) pressCase(ic, jc, FALSE);
else if (_autoreveal) pressClearFunction(ic, jc, FALSE);
}
void Field::showMines()
......@@ -325,7 +349,7 @@ void Field::showMines()
for(uint i=1; i<=lev.width; i++)
for(uint j=1; j<=lev.height; j++)
if ( (pfield(i, j) & MINE) ) {
if ( !(pfield(i, j) & EXPLODED) && !(pfield(i, j) & MARKED) )
if ( !(pfield(i, j) & EXPLODED) && !(pfield(i, j) & MARKED) )
changeCaseState(i,j,UNCOVERED);
} else if (pfield(i, j) & MARKED) changeCaseState(i, j, ERROR);
}
......@@ -351,10 +375,23 @@ void Field::pressClearFunction(uint i, uint j, bool pressed)
#define M_OR_U(i, j) ( (pfield(i, j) & MARKED) || (pfield(i, j) & UNCERTAIN) )
void Field::keyboardAutoReveal()
{
pressClearFunction(ic, jc, FALSE);
QTimer::singleShot(50, this, SLOT(keyboardAutoRevealSlot()));
}
void Field::keyboardAutoRevealSlot()
{
pressClearFunction(ic, jc, TRUE);
drawCursor(TRUE);
autoReveal();
}
void Field::autoReveal()
{
if ( locked() ) return;
if ( pfield(ic, jc) & (COVERED|MARKED|UNCERTAIN) ) return;
if ( state!=Playing ) return;
if ( pfield(ic, jc) & (COVERED|MARKED|UNCERTAIN) ) return;
/* number of mines around the case */