337 lines
9.7 KiB
C++
337 lines
9.7 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2008 by Oliver Groß *
|
|
* z.o.gross@gmx.de *
|
|
* *
|
|
* 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., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
#include <QApplication>
|
|
#include <QStatusBar>
|
|
#include <QMenuBar>
|
|
#include <QMenu>
|
|
#include <QToolBar>
|
|
#include <QAction>
|
|
#include <QFileDialog>
|
|
#include <QMessageBox>
|
|
#include <QDir>
|
|
#include <QInputDialog>
|
|
|
|
#include <libqnono/cnonogram.h>
|
|
#include <libqnono/cnonogramsolver.h>
|
|
#include <libqnono/ccrosspackage.h>
|
|
|
|
#include "constants.h"
|
|
#include "cgamewindow.h"
|
|
#include "ccrossfieldwidget.h"
|
|
#include "cnewgamedialog.h"
|
|
#include "chighscore.h"
|
|
|
|
namespace qcross {
|
|
using namespace libqnono;
|
|
//public
|
|
CGameWindow::CGameWindow(QWidget * parent) : QMainWindow(parent) {
|
|
m_Highscore = NULL;
|
|
m_PictureIndex = -1;
|
|
m_Picture = new CNonogram(QSize(10, 10));
|
|
m_Picture->updateNumbers();
|
|
|
|
m_Field = new CCrossFieldWidget(m_Picture, this);
|
|
m_Field->setTime(42 * 60 + 23);
|
|
|
|
setCentralWidget(m_Field);
|
|
m_Field->showMessage(tr("Welcome to QCross!"));
|
|
|
|
createActions();
|
|
// statusBar()->show();
|
|
m_Solver = new CNonogramSolver(this);
|
|
|
|
connect(m_Solver, SIGNAL(markRequested(int, int, int)), m_Field, SLOT(mark(int, int, int)));
|
|
connect(m_Field, SIGNAL(solved()), this, SLOT(wonGame()));
|
|
connect(m_Field, SIGNAL(timeUp()), this, SLOT(lostGame()));
|
|
connect(m_Field, SIGNAL(markError()), this, SLOT(handleErrorMark()));
|
|
}
|
|
|
|
CGameWindow::~CGameWindow() {
|
|
m_Field->setPicture(NULL);
|
|
delete m_Picture;
|
|
}
|
|
|
|
//protected
|
|
void CGameWindow::createActions() {
|
|
QToolBar * currentToolBar;
|
|
QMenu * currentMenu;
|
|
|
|
//Game
|
|
currentMenu = menuBar()->addMenu(tr("&Game"));
|
|
currentMenu->addAction(tr("&New..."), this, SLOT(newGame()), Qt::CTRL + Qt::Key_N);
|
|
currentMenu->addSeparator();
|
|
m_SaveGameAction = currentMenu->addAction(tr("&Save..."), this, SLOT(saveGame()), Qt::CTRL + Qt::Key_N);
|
|
m_SaveGameAction->setEnabled(false);
|
|
currentMenu->addAction(tr("&Load..."), this, SLOT(loadGame()), Qt::CTRL + Qt::Key_N);
|
|
currentMenu->addSeparator();
|
|
m_RestartGameAction = currentMenu->addAction(tr("&Restart"), this, SLOT(restartGame()), Qt::CTRL + Qt::Key_R);
|
|
m_RestartGameAction->setEnabled(false);
|
|
m_PauseGameAction = currentMenu->addAction(tr("&Pause"));
|
|
m_PauseGameAction->setEnabled(false);
|
|
m_PauseGameAction->setCheckable(true);
|
|
m_PauseGameAction->setChecked(true);
|
|
m_PauseGameAction->setShortcut(Qt::CTRL + Qt::Key_P);
|
|
connect(m_PauseGameAction, SIGNAL(toggled(bool)), this, SLOT(pauseGame(bool)));
|
|
currentMenu->addSeparator();
|
|
currentMenu->addAction(tr("&Quit"), this, SLOT(close()), Qt::CTRL + Qt::Key_Q);
|
|
currentMenu->addSeparator();
|
|
currentMenu->addAction(tr("debug: solve"), this, SLOT(startSolver()));
|
|
|
|
currentToolBar = addToolBar(currentMenu->title());
|
|
currentToolBar->addActions(currentMenu->actions());
|
|
|
|
//Help
|
|
currentMenu = menuBar()->addMenu(tr("&Help"));
|
|
currentMenu->addAction(tr("&About"), this, SLOT(about()));
|
|
currentMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt()));
|
|
}
|
|
|
|
const char magicSaveGameHeader[] = {'C', 'R', 'S', 'V'};
|
|
|
|
bool CGameWindow::readSaveGame(QDataStream & stream) {
|
|
QString stringBuffer;
|
|
qint32 intBuffer;
|
|
QSize sizeBuffer;
|
|
|
|
// picture index
|
|
stream >> intBuffer; qDebug("picture index = %i", m_PictureIndex);
|
|
if (stream.atEnd()) {
|
|
qCritical("invalid savegame");
|
|
return false;
|
|
}
|
|
m_PictureIndex = intBuffer;
|
|
|
|
// package name
|
|
if (m_PictureIndex > -1) {
|
|
stream >> stringBuffer; qDebug("package name = %s", qPrintable(stringBuffer));
|
|
if (stream.atEnd()) {
|
|
qCritical("invalid savegame");
|
|
return false;
|
|
}
|
|
m_PackageName = stringBuffer;
|
|
|
|
stream >> stringBuffer; qDebug("highscoreFileName = %s", qPrintable(stringBuffer));
|
|
if (stream.atEnd()) {
|
|
qCritical("invalid savegame");
|
|
return false;
|
|
}
|
|
|
|
if (m_Highscore)
|
|
delete m_Highscore;
|
|
|
|
m_Highscore = new CHighscore(0);
|
|
m_Highscore->setFileName(QCROSS_STRING_DATAPATH + QDir::separator() + stringBuffer);
|
|
m_Highscore->open();
|
|
}
|
|
|
|
// picture
|
|
if (m_Picture)
|
|
delete m_Picture;
|
|
|
|
m_Picture = new CNonogram();
|
|
|
|
if (!m_Picture->readFromStream(stream)) {
|
|
qCritical("invalid savegame");
|
|
return false;
|
|
}
|
|
|
|
// TODO handle save game porting properly
|
|
// TODO check for matching nonogram
|
|
|
|
m_Field->setPicture(m_Picture);
|
|
m_Field->applyState(stream);
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGameWindow::writeSaveGame(QDataStream & stream) {
|
|
// picture index
|
|
stream << qint32(m_PictureIndex);
|
|
if (m_PictureIndex > -1) {
|
|
stream << m_PackageName;
|
|
stream << m_Highscore->fileName().section(QDir::separator(), -1);
|
|
}
|
|
|
|
m_Picture->writeToStream(stream);
|
|
m_Field->dumpState(stream);
|
|
}
|
|
|
|
void CGameWindow::newGame() {
|
|
bool notPaused = !m_Field->isPaused();
|
|
if (notPaused)
|
|
pauseGame(true);
|
|
|
|
CNewGameDialog dialog;
|
|
if (dialog.exec() == QDialog::Accepted) {
|
|
if (m_Highscore)
|
|
delete m_Highscore;
|
|
|
|
m_Highscore = dialog.takeHighscore();
|
|
m_PackageName = dialog.selectedPackageName();
|
|
m_PictureIndex = m_Highscore ? dialog.nonogramIndex() : -1;
|
|
|
|
CNonogram * newPicture = dialog.takeNonogram();
|
|
newPicture->updateNumbers();
|
|
|
|
m_PauseGameAction->setEnabled(true);
|
|
m_RestartGameAction->setEnabled(true);
|
|
|
|
m_Field->setPicture(newPicture);
|
|
|
|
if (m_Picture)
|
|
delete m_Picture;
|
|
|
|
m_Picture = newPicture;
|
|
|
|
m_Field->resume();
|
|
m_Field->showMessage(tr("Game started!"), 1000);
|
|
}
|
|
else
|
|
pauseGame(false);
|
|
}
|
|
|
|
|
|
void CGameWindow::saveGame() {
|
|
if (!m_Picture)
|
|
return;
|
|
|
|
if (!m_Field->isPaused())
|
|
m_PauseGameAction->setChecked(true);
|
|
|
|
QString fileName = QFileDialog::getSaveFileName(this, tr("Save current game"),
|
|
QDir::homePath(), tr("QCross Saved games (*.csg)"));
|
|
|
|
qDebug("saving game state file: %s", fileName.toAscii().data());
|
|
QFile file(fileName);
|
|
if (!file.open(QIODevice::WriteOnly))
|
|
return;
|
|
|
|
QDataStream out(&file);
|
|
out.setVersion(QDataStream::Qt_4_0);
|
|
|
|
if (out.writeRawData(magicSaveGameHeader, 4) == -1)
|
|
qDebug("could not write magic save game header");
|
|
else
|
|
writeSaveGame(out);
|
|
|
|
m_PauseGameAction->setChecked(false);
|
|
}
|
|
|
|
void CGameWindow::loadGame() {
|
|
if (!m_Field->isPaused())
|
|
m_PauseGameAction->setChecked(true);
|
|
|
|
QString fileName = QFileDialog::getOpenFileName(this, tr("Open saved game"),
|
|
QDir::homePath(), tr("QCross Saved games (*.csg)"));
|
|
|
|
qDebug("opening game state file: %s", fileName.toAscii().data());
|
|
QFile file(fileName);
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
return;
|
|
|
|
QDataStream in(&file);
|
|
in.setVersion(QDataStream::Qt_4_0);
|
|
|
|
char magicHeader[4];
|
|
in.readRawData(magicHeader, 4);
|
|
|
|
qDebug("checking magic savegame header");
|
|
for (int i = 0; i < 4; i++) {
|
|
if (magicHeader[i] != magicSaveGameHeader[i])
|
|
return;
|
|
}
|
|
|
|
if (readSaveGame(in)) {
|
|
m_PauseGameAction->setEnabled(true);
|
|
}
|
|
else {
|
|
m_Field->setPicture(NULL);
|
|
m_PictureIndex = -1;
|
|
|
|
if (m_Picture) {
|
|
delete m_Picture;
|
|
m_Picture = NULL;
|
|
}
|
|
|
|
if (m_Highscore) {
|
|
delete m_Highscore;
|
|
m_Highscore = NULL;
|
|
}
|
|
}
|
|
|
|
m_PauseGameAction->setChecked(false);
|
|
}
|
|
|
|
void CGameWindow::restartGame() {
|
|
m_Field->setTime(m_Picture->timeout());
|
|
|
|
m_PauseGameAction->setChecked(false);
|
|
m_Field->start();
|
|
|
|
m_Field->showMessage(tr("Game restarted."), 1000);
|
|
}
|
|
|
|
void CGameWindow::pauseGame(bool value) {
|
|
if (value) {
|
|
m_Field->pause();
|
|
m_Field->showMessage(tr("Game paused."));
|
|
}
|
|
else {
|
|
m_Field->showMessage(tr("Game resumed."), 1000);
|
|
m_Field->resume();
|
|
}
|
|
}
|
|
|
|
void CGameWindow::wonGame() {
|
|
m_PauseGameAction->setEnabled(false);
|
|
m_Field->showMessage(tr("Congratulations! You've solved the puzzle."));
|
|
if (m_Highscore) {
|
|
qDebug("attempting to save highscore");
|
|
(*m_Highscore)[m_PictureIndex] = m_Field->time();
|
|
m_Highscore->save();
|
|
}
|
|
}
|
|
|
|
void CGameWindow::lostGame() {
|
|
m_PauseGameAction->setEnabled(false);
|
|
m_Field->showMessage(tr("Too bad! Time's up."), 0, CCrossFieldWidget::Critical);
|
|
}
|
|
|
|
void CGameWindow::handleErrorMark() {
|
|
int timeDiff = (m_Field->errorCount() == 1) ? 2 : ((m_Field->errorCount() == 2) ? 4 : 8);
|
|
|
|
m_Field->showMessage(tr("Sorry this was not correct: -%1min").arg(timeDiff), 1000, CCrossFieldWidget::Warning);
|
|
|
|
m_Field->setTime(m_Field->time() - timeDiff*60);
|
|
}
|
|
|
|
void CGameWindow::about() {
|
|
QMessageBox::about(this, tr("About"), tr("This is a still unfancy gui for solving nonograms."));
|
|
}
|
|
|
|
void CGameWindow::startSolver() {
|
|
m_Solver->setNonogram(m_Picture);
|
|
|
|
restartGame();
|
|
|
|
m_Solver->solve();
|
|
}
|
|
}
|