/* * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) * * This program 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. */ #include "field.h" #include "field.moc" #include #include #include #include #include #include #include "settings.h" #include "solver/solver.h" #include "dialogs.h" using namespace KGrid2D; const Field::ActionData Field::ACTION_DATA[Nb_Actions] = { { "Reveal", "reveal", I18N_NOOP("Case revealed") }, { "AutoReveal", "autoreveal", I18N_NOOP("Case autorevealed") }, { "SetFlag", "mark", I18N_NOOP("Flag set") }, { "UnsetFlag", "unmark", I18N_NOOP("Flag unset") }, { "SetUncertain", "set_uncertain", I18N_NOOP("Question mark set") }, { "UnsetUncertain", "unset_uncertain", I18N_NOOP("Question mark unset") } }; Field::Field(TQWidget *parent) : FieldFrame(parent), _state(Init), _solvingState(Regular), _level(Level::Easy) {} void Field::readSettings() { if ( inside(_cursor) ) { TQPainter p(this); drawCase(p, _cursor); } if ( Settings::magicReveal() ) emit setCheating(); } TQSize Field::sizeHint() const { return TQSize(2*frameWidth() + _level.width()*Settings::caseSize(), 2*frameWidth() + _level.height()*Settings::caseSize()); } void Field::setLevel(const Level &level) { _level = level; reset(false); adjustSize(); } void Field::setReplayField(const TQString &field) { setState(Replaying); initReplay(field); } void Field::setState(GameState state) { Q_ASSERT( state!=GameOver ); emit gameStateChanged(state); _state = state; } void Field::reset(bool init) { BaseField::reset(_level.width(), _level.height(), _level.nbMines()); if ( init || _state==Init ) setState(Init); else setState(Stopped); if (Settings::magicReveal()) emit setCheating(); _currentAction = Settings::EnumMouseAction::None; _reveal = false; _cursor.first = _level.width()/2; _cursor.second = _level.height()/2; _advisedCoord = Coord(-1, -1); update(); } void Field::paintEvent(TQPaintEvent *e) { TQPainter painter(this); drawFrame(&painter); if ( _state==Paused ) return; Coord min = fromPoint(e->rect().topLeft()); bound(min); Coord max = fromPoint(e->rect().bottomRight()); bound(max); for (short i=min.first; i<=max.first; i++) for (short j=min.second; j<=max.second; j++) drawCase(painter, Coord(i,j)); } void Field::changeCase(const Coord &p, CaseState newState) { BaseField::changeCase(p, newState); TQPainter painter(this); drawCase(painter, p); if ( isActive() ) emit updateStatus( hasMine(p) ); } TQPoint Field::toPoint(const Coord &p) const { TQPoint qp; qp.setX( p.first*Settings::caseSize() + frameWidth() ); qp.setY( p.second*Settings::caseSize() + frameWidth() ); return qp; } Coord Field::fromPoint(const TQPoint &qp) const { double i = (double)(qp.x() - frameWidth()) / Settings::caseSize(); double j = (double)(qp.y() - frameWidth()) / Settings::caseSize(); return Coord((int)floor(i), (int)floor(j)); } int Field::mapMouseButton(TQMouseEvent *e) const { switch (e->button()) { case Qt::LeftButton: return Settings::mouseAction(Settings::EnumButton::left); case Qt::MidButton: return Settings::mouseAction(Settings::EnumButton::mid); case Qt::RightButton: return Settings::mouseAction(Settings::EnumButton::right); default: return Settings::EnumMouseAction::ToggleFlag; } } void Field::revealActions(bool press) { if ( _reveal==press ) return; // avoid flicker _reveal = press; switch (_currentAction) { case Reveal: pressCase(_cursor, press); break; case AutoReveal: pressClearFunction(_cursor, press); break; default: break; } } void Field::mousePressEvent(TQMouseEvent *e) { if ( !isActive() || (_currentAction!=Settings::EnumMouseAction::None) ) return; emit setMood(Stressed); _currentAction = mapMouseButton(e); Coord p = fromPoint(e->pos()); if ( !inside(p) ) return; placeCursor(p); revealActions(true); } void Field::mouseReleaseEvent(TQMouseEvent *e) { if ( !isActive() ) return; int tmp = _currentAction; emit setMood(Normal); revealActions(false); int ma = mapMouseButton(e); _currentAction = Settings::EnumMouseAction::None; if ( ma!=tmp ) return; Coord p = fromPoint(e->pos()); if ( !inside(p) ) return; placeCursor(p); switch (ma) { case Settings::EnumMouseAction::ToggleFlag: doMark(p); break; case Settings::EnumMouseAction::ToggleUncertainFlag: doUmark(p); break; case Settings::EnumMouseAction::Reveal: doReveal(p); break; case Settings::EnumMouseAction::AutoReveal: doAutoReveal(p); break; default: break; } } void Field::mouseMoveEvent(TQMouseEvent *e) { if ( !isActive() ) return; Coord p = fromPoint(e->pos()); if ( p==_cursor ) return; // avoid flicker revealActions(false); if ( !inside(p) ) return; placeCursor(p); revealActions(true); } void Field::pressCase(const Coord &c, bool pressed) { if ( state(c)==Covered ) { TQPainter painter(this); drawCase(painter, c, pressed); } } void Field::pressClearFunction(const Coord &p, bool pressed) { pressCase(p, pressed); CoordList n = coveredNeighbours(p); TQPainter painter(this); for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) drawCase(painter, *it, pressed); } void Field::keyboardAutoReveal() { _cursor_back = _cursor; pressClearFunction(_cursor_back, true); TQTimer::singleShot(50, this, TQT_SLOT(keyboardAutoRevealSlot())); } void Field::keyboardAutoRevealSlot() { pressClearFunction(_cursor_back, false); doAutoReveal(_cursor_back); } void Field::doAutoReveal(const Coord &c) { if ( !isActive() ) return; if ( state(c)!=Uncovered ) return; emit addAction(c, AutoReveal); resetAdvised(); doAction(AutoReveal, c, Settings::magicReveal()); } void Field::pause() { switch (_state) { case Paused: setState(Playing); break; case Playing: setState(Paused); break; default: return; } update(); } void Field::moveCursor(Neighbour n) { Coord c = neighbour(_cursor, n); if ( inside(c) ) placeCursor(c); } void Field::moveToEdge(Neighbour n) { Coord c = toEdge(_cursor, n); if ( inside(c) ) placeCursor(c); } bool Field::doReveal(const Coord &c, CoordList *autorevealed, bool *caseUncovered) { if ( !isActive() ) return true; if ( state(c)!=Covered ) return true; if ( firstReveal() ) setState(Playing); CaseState state = doAction(Reveal, c, Settings::magicReveal(), autorevealed, caseUncovered); emit addAction(c, Reveal); return ( state!=Error ); } void Field::doMark(const Coord &c) { if ( !isActive() ) return; ActionType action; CaseState oldState = state(c); switch (oldState) { case Covered: action = SetFlag; break; case Marked: action = (Settings::uncertainMark() ? SetUncertain : UnsetFlag); break; case Uncertain: action = UnsetUncertain; break; default: return; } CaseState newState = doAction(action, c, Settings::magicReveal()); addMarkAction(c, newState, oldState); } void Field::doUmark(const Coord &c) { if ( !isActive() ) return; ActionType action; CaseState oldState = state(c); switch (oldState) { case Covered: case Marked: action = SetUncertain; break; case Uncertain: action = UnsetUncertain; break; default: return; } CaseState newState = doAction(action, c, Settings::magicReveal()); addMarkAction(c, newState, oldState); } KMines::CaseState Field::doAction(ActionType type, const Coord &c, bool complete, CoordList *autorevealed, bool *caseUncovered) { resetAdvised(); CaseState state = Error; if ( _solvingState==Solved ) complete = false; KNotifyClient::event(winId(), ACTION_DATA[type].event, i18n(ACTION_DATA[type].eventMessage)); switch (type) { case Reveal: if ( !reveal(c, autorevealed, caseUncovered) ) emit gameStateChanged(GameOver); else { state = Uncovered; if (complete) completeReveal(); } break; case AutoReveal: if ( !autoReveal(c, caseUncovered) ) emit gameStateChanged(GameOver); else { state = Uncovered; if (complete) completeReveal(); } break; case SetFlag: state = Marked; if (complete) completeReveal(); break; case UnsetFlag: case UnsetUncertain: state = Covered; break; case SetUncertain: state = Uncertain; break; case Nb_Actions: Q_ASSERT(false); break; } if ( state!=Error ) changeCase(c, state); return state; } void Field::addMarkAction(const Coord &c, CaseState newS, CaseState oldS) { switch (newS) { case Marked: emit addAction(c, SetFlag); return; case Uncertain: emit addAction(c, SetUncertain); return; default: break; } switch (oldS) { case Marked: emit addAction(c, UnsetFlag); return; case Uncertain: emit addAction(c, UnsetUncertain); return; default: break; } } void Field::placeCursor(const Coord &p) { if ( !isActive() ) return; Q_ASSERT( inside(p) ); Coord old = _cursor; _cursor = p; if ( Settings::keyboardGame() ) { TQPainter painter(this); drawCase(painter, old); drawCase(painter, _cursor); } } void Field::resetAdvised() { if ( !inside(_advisedCoord) ) return; TQPainter p(this); Coord tmp = _advisedCoord; _advisedCoord = Coord(-1, -1); drawCase(p, tmp); } void Field::setAdvised(const Coord &c, double proba) { resetAdvised(); _solvingState = Advised; _advisedCoord = c; _advisedProba = proba; if ( inside(c) ) { TQPainter p(this); drawCase(p, c); } } void Field::drawCase(TQPainter &painter, const Coord &c, bool pressed) const { Q_ASSERT( inside(c) ); TQString text; uint nbMines = 0; PixmapType type = NoPixmap; switch ( state(c) ) { case Covered: break; case Marked: type = FlagPixmap; pressed = false; break; case Error: type = ErrorPixmap; pressed = true; break; case Uncertain: text = '?'; pressed = false; break; case Exploded: type = ExplodedPixmap; pressed = true; break; case Uncovered: pressed = true; if ( hasMine(c) ) type = MinePixmap; else { nbMines = nbMinesAround(c); if (nbMines) text.setNum(nbMines); } } int i = -1; if ( c==_advisedCoord ) { if ( _advisedProba==1 ) i = 0; else if ( _advisedProba>0.75 ) i = 1; else if ( _advisedProba>0.5 ) i = 2; else if ( _advisedProba>0.25 ) i = 3; else i = 4; } bool hasFocus = ( Settings::keyboardGame() && (c==_cursor) ); drawBox(painter, toPoint(c), pressed, type, text, nbMines, i, hasFocus); }