/* * 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 "status.h" #include "status.moc" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "solver/solver.h" #include "dialogs.h" #include "version.h" Status::Status(TQWidget *parent) : TQWidget(parent, "status"), _oldLevel(Level::Easy) { _timer = new TQTimer(this); connect(_timer, TQT_SIGNAL(timeout()), TQT_SLOT(replayStep())); _solver = new Solver(this); connect(_solver, TQT_SIGNAL(solvingDone(bool)), TQT_SLOT(solvingDone(bool))); // top layout TQGridLayout *top = new TQGridLayout(this, 2, 5, 10, 10); top->setColStretch(1, 1); top->setColStretch(3, 1); // status bar // mines left LCD left = new KGameLCD(5, this); left->setFrameStyle(TQFrame::Panel | TQFrame::Sunken); left->setDefaultBackgroundColor(black); left->setDefaultColor(white); TQWhatsThis::add(left, i18n("Mines left.
" "It turns red " "when you have flagged more cases than " "present mines.
")); top->addWidget(left, 0, 0); // smiley smiley = new Smiley(this); connect(smiley, TQT_SIGNAL(clicked()), TQT_SLOT(smileyClicked())); smiley->setFocusPolicy(TQWidget::NoFocus); TQWhatsThis::add(smiley, i18n("Press to start a new game")); top->addWidget(smiley, 0, 2); // digital clock LCD dg = new DigitalClock(this); TQWhatsThis::add(dg, i18n("Time elapsed.
" "It turns blue " "if it is a highscore " "and red " "if it is the best time.
")); top->addWidget(dg, 0, 4); // mines field _fieldContainer = new TQWidget(this); TQGridLayout *g = new TQGridLayout(_fieldContainer, 1, 1); _field = new Field(_fieldContainer); _field->readSettings(); g->addWidget(_field, 0, 0, AlignCenter); connect( _field, TQT_SIGNAL(updateStatus(bool)), TQT_SLOT(updateStatus(bool)) ); connect(_field, TQT_SIGNAL(gameStateChanged(GameState)), TQT_SLOT(gameStateChangedSlot(GameState)) ); connect(_field, TQT_SIGNAL(setMood(Mood)), smiley, TQT_SLOT(setMood(Mood))); connect(_field, TQT_SIGNAL(setCheating()), dg, TQT_SLOT(setCheating())); connect(_field,TQT_SIGNAL(addAction(const KGrid2D::Coord &, Field::ActionType)), TQT_SLOT(addAction(const KGrid2D::Coord &, Field::ActionType))); TQWhatsThis::add(_field, i18n("Mines field.")); // resume button _resumeContainer = new TQWidget(this); g = new TQGridLayout(_resumeContainer, 1, 1); TQFont f = font(); f.setBold(true); TQPushButton *pb = new TQPushButton(i18n("Press to Resume"), _resumeContainer); pb->setFont(f); connect(pb, TQT_SIGNAL(clicked()), TQT_SIGNAL(pause())); g->addWidget(pb, 0, 0, AlignCenter); _stack = new TQWidgetStack(this); _stack->addWidget(_fieldContainer); _stack->addWidget(_resumeContainer); _stack->raiseWidget(_fieldContainer); top->addMultiCellWidget(_stack, 1, 1, 0, 4); } void Status::smileyClicked() { if ( _field->gameState()==Paused ) emit pause(); else restartGame(); } void Status::newGame(int t) { if ( _field->gameState()==Paused ) emit pause(); Level::Type type = (Level::Type)t; Settings::setLevel(type); if ( type!=Level::Custom ) newGame( Level(type) ); else newGame( Settings::customLevel() ); } void Status::newGame(const Level &level) { _timer->stop(); if ( level.type()!=Level::Custom ) KExtHighscore::setGameType(level.type()); _field->setLevel(level); } bool Status::checkBlackMark() { bool bm = ( _field->gameState()==Playing ); if (bm) KExtHighscore::submitScore(KExtHighscore::Lost, this); return bm; } void Status::restartGame() { if ( _field->gameState()==Paused ) emit pause(); else if ( _field->gameState()==Replaying ) { _timer->stop(); _field->setLevel(_oldLevel); } else { bool bm = checkBlackMark(); _field->reset(bm); } } void Status::settingsChanged() { _field->readSettings(); if ( Settings::level()!=Level::Custom ) return; Level l = Settings::customLevel(); if ( l==_field->level() ) return; if ( _field->gameState()==Paused ) emit pause(); newGame(l); } void Status::updateStatus(bool mine) { int r = _field->nbMines() - _field->nbMarked(); TQColor color = (r<0 && !_field->isSolved() ? red : white); left->setColor(color); left->display(r); if ( _field->isSolved() && !mine ) gameStateChanged(GameOver, true); // ends only for wins } void Status::setGameOver(bool won) { if ( !won ) KNotifyClient::event(winId(), "explosion", i18n("Explosion!")); _field->showAllMines(won); smiley->setMood(won ? Happy : Sad); if ( _field->gameState()==Replaying ) return; _field->setGameOver(); dg->stop(); if ( _field->level().type()!=Level::Custom && !dg->cheating() ) { if (won) KExtHighscore::submitScore(dg->score(), this); else KExtHighscore::submitScore(KExtHighscore::Lost, this); } KNotifyClient::event(winId(), won ? "won" : "lost", won ? i18n("Game won!") : i18n("Game lost!")); // game log _logRoot.setAttribute("count", dg->nbActions()); if ( Settings::magicReveal() ) _logRoot.setAttribute("complete_reveal", "true"); TQString sa = "none"; if ( _field->solvingState()==Solved ) sa = "solving"; else if ( _field->solvingState()==Advised ) sa = "advising"; _logRoot.setAttribute("solver", sa); TQDomElement f = _log.createElement("Field"); _logRoot.appendChild(f); TQDomText data = _log.createTextNode(_field->string()); f.appendChild(data); } void Status::setStopped() { smiley->setMood(Normal); updateStatus(false); bool custom = ( _field->level().type()==Level::Custom ); dg->reset(custom); _field->setSolvingState(Regular); } void Status::setPlaying() { smiley->setMood(Normal); dg->start(); if ( _field->gameState()==Paused ) return; // do not restart game log... // game log const Level &level = _field->level(); _log = TQDomDocument("kmineslog"); _logRoot = _log.createElement("kmineslog"); _logRoot.setAttribute("version", SHORT_VERSION); TQDateTime date = TQDateTime::currentDateTime(); _logRoot.setAttribute("date", date.toString(Qt::ISODate)); _logRoot.setAttribute("width", level.width()); _logRoot.setAttribute("height", level.height()); _logRoot.setAttribute("mines", level.nbMines()); _log.appendChild(_logRoot); _logList = _log.createElement("ActionList"); _logRoot.appendChild(_logList); } void Status::gameStateChanged(GameState state, bool won) { TQWidget *w = _fieldContainer; switch (state) { case Playing: setPlaying(); break; case GameOver: setGameOver(won); break; case Paused: smiley->setMood(Sleeping); dg->stop(); w = _resumeContainer; break; case Stopped: case Init: setStopped(); break; case Replaying: smiley->setMood(Normal); break; case NB_STATES: Q_ASSERT(false); break; } _stack->raiseWidget(w); emit gameStateChangedSignal(state); } void Status::addAction(const KGrid2D::Coord &c, Field::ActionType type) { TQDomElement action = _log.createElement("Action"); action.setAttribute("time", dg->pretty()); action.setAttribute("column", c.first); action.setAttribute("line", c.second); action.setAttribute("type", Field::ACTION_DATA[type].name); _logList.appendChild(action); dg->addAction(); } void Status::advise() { int res = KMessageBox::warningContinueCancel(this, i18n("When the solver gives " "you advice, your score will not be added to the highscores."), TQString::null, TQString::null, "advice_warning"); if ( res==KMessageBox::Cancel ) return; dg->setCheating(); float probability; KGrid2D::Coord c = _solver->advise(*_field, probability); _field->setAdvised(c, probability); } void Status::solve() { dg->setCheating(); _solver->solve(*_field, false); _field->setSolvingState(Solved); } void Status::solvingDone(bool success) { if ( !success ) gameStateChanged(GameOver, false); } void Status::solveRate() { SolvingRateDialog sd(*_field, this); sd.exec(); } void Status::viewLog() { KDialogBase d(this, "view_log", true, i18n("View Game Log"), KDialogBase::Close, KDialogBase::Close); TQTextEdit *view = new TQTextEdit(&d); view->setReadOnly(true); view->setTextFormat(PlainText); view->setText(_log.toString()); d.setMainWidget(view); d.resize(500, 400); d.exec(); } void Status::saveLog() { KURL url = KFileDialog::getSaveURL(TQString::null, TQString::null, this); if ( url.isEmpty() ) return; if ( KIO::NetAccess::exists(url, false, this) ) { KGuiItem gi = KStdGuiItem::save(); gi.setText(i18n("Overwrite")); int res = KMessageBox::warningYesNo(this, i18n("The file already exists. Overwrite?"), i18n("File Exists"), gi, KStdGuiItem::cancel()); if ( res==KMessageBox::No ) return; } KTempFile tmp; (*tmp.textStream()) << _log.toString(); tmp.close(); KIO::NetAccess::upload(tmp.name(), url, this); tmp.unlink(); } void Status::loadLog() { KURL url = KFileDialog::getOpenURL(TQString::null, TQString::null, this); if ( url.isEmpty() ) return; TQString tmpFile; bool success = false; TQDomDocument doc; if( KIO::NetAccess::download(url, tmpFile, this) ) { TQFile file(tmpFile); if ( file.open(IO_ReadOnly) ) { int errorLine; bool ok = doc.setContent(&file, 0, &errorLine); if ( !ok ) { KMessageBox::sorry(this, i18n("Cannot read XML file on line %1") .arg(errorLine)); return; } success = true; } KIO::NetAccess::removeTempFile(tmpFile); } if ( !success ) { KMessageBox::sorry(this, i18n("Cannot load file.")); return; } if ( !checkLog(doc) ) KMessageBox::sorry(this, i18n("Log file not recognized.")); else { _log = doc; _logRoot = doc.namedItem("kmineslog").toElement(); emit gameStateChangedSignal(GameOver); } } bool Status::checkLog(const TQDomDocument &doc) { // check root element if ( doc.doctype().name()!="kmineslog" ) return false; TQDomElement root = doc.namedItem("kmineslog").toElement(); if ( root.isNull() ) return false; bool ok; uint w = root.attribute("width").toUInt(&ok); if ( !ok || w>CustomConfig::maxWidth || wCustomConfig::maxHeight || hLevel::maxNbMines(w, h) ) return false; // check field TQDomElement field = root.namedItem("Field").toElement(); if ( field.isNull() ) return false; TQString ftext = field.text(); if ( !BaseField::checkField(w, h, nb, ftext) ) return false; // check action list TQDomElement list = root.namedItem("ActionList").toElement(); if ( list.isNull() ) return false; TQDomNodeList actions = list.elementsByTagName("Action"); if ( actions.count()==0 ) return false; for (uint i=0; i=h ) return false; uint j = a.attribute("column").toUInt(&ok); if ( !ok || j>=w ) return false; TQString type = a.attribute("type"); uint k = 0; for (; klevel(); newGame(level); _field->setReplayField(f.toElement().text()); TQString s = _logRoot.attribute("complete_reveal"); _completeReveal = ( s=="true" ); f = _logRoot.namedItem("ActionList"); _actions = f.toElement().elementsByTagName("Action"); _index = 0; _timer->start(500); } void Status::replayStep() { if ( _index>=_actions.count() ) { _timer->stop(); _actions = TQDomNodeList(); return; } _timer->changeInterval(200); TQDomElement a = _actions.item(_index).toElement(); dg->setTime(a.attribute("time")); uint i = a.attribute("column").toUInt(); uint j = a.attribute("line").toUInt(); TQString type = a.attribute("type"); for (uint k=0; kdoAction((Field::ActionType)k, KGrid2D::Coord(i, j), _completeReveal); break; } _index++; }