qcross/qcross/cgamewindow.cpp

347 lines
9.9 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 = NULL;
// CNonogram * dummyPic = new CNonogram(QSize(10, 10));
// dummyPic->updateNumbers();
m_Field = new CCrossFieldWidget(NULL, 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->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)
m_Field->pause();
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_SaveGameAction->setEnabled(true);
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);
}
if (m_Picture && !m_PauseGameAction->isChecked())
m_Field->resume();
}
void CGameWindow::saveGame() {
if (!m_Picture)
return;
if (!m_Field->isPaused())
m_Field->pause();
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);
if(!m_PauseGameAction->isChecked())
m_Field->resume();
}
void CGameWindow::loadGame() {
if (!m_Field->isPaused())
m_Field->pause();
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);
m_PauseGameAction->setChecked(m_Field->isPaused());
m_SaveGameAction->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;
}
}
if (m_Picture && !m_PauseGameAction->isChecked())
m_Field->resume();
}
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->isPaused())
return;
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();
}
}