/*************************************************************************** Description : KPuzzle - A KDE Jigsaw Puzzle Game Version : 0.2 Copyright : (C) 2000-2001 by Michael Wand EMail : mwand@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. * * * ***************************************************************************/ #include "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kpuzzlegame.h" #include "kpuzzle.h" #include "kpuzzleview.h" #include "gamewidget.h" #include "piecewidget.h" #include "picview.h" #include "gamedialog.h" #include "savedialog.h" #include "highscore.h" #include "piece.h" #include "prefs.h" #include #include #include #include /* Why to change a score */ #define SC_PIECE_RIGHT 1 #define SC_PIECE_WRONG 2 #define SC_COUNT_TIME 3 #define SC_BONUS 4 /* Save all data contained in this class */ bool ScoreData::save(QDataStream*) const { return true; } /* Load all data contained in this class */ bool ScoreData::load(QDataStream*) { return true; } /* Save all data contained in this class */ bool GameData::save(QDataStream* f) const { *f << _filename; *f << (Q_INT8) _savePixmap; *f << _originalPixmapSize; *f << _annotation; *f << (Q_INT8) _gameType; *f << (Q_INT8) _difficulty; *f << (Q_INT8) _scale; *f << (Q_INT8) _useMask; *f << (Q_INT8) _dialog_pieceSize; *f << _displace; *f << _pieceSize; *f << _piecesCount; return true; } /* Load all data contained in this class */ bool GameData::load(QDataStream* f) { *f >> _filename; *f >> (Q_INT8&) _savePixmap; *f >> _originalPixmapSize; *f >> _annotation; *f >> (Q_INT8&) _gameType; *f >> (Q_INT8&) _difficulty; *f >> (Q_INT8&) _scale; *f >> (Q_INT8&) _useMask; *f >> (Q_INT8&) _dialog_pieceSize; *f >> _displace; *f >> _pieceSize; *f >> _piecesCount; return true; } /* Save all data contained in this class */ bool CurrentData::save(QDataStream* f) const { /* The current piece should be saved. As we cannot save the pointer, * we determine the number of this piece and save * that number. */ Q_INT32 currentPieceNr=-1; /* Iterator. */ int i; *f << _score; *f << _faults; *f << _rounds; *f << _elapsed; *f << (Q_INT8)_dirty; Q_INT32 cnt = _pieceList->count(); *f << cnt; QPtrListIterator savingIterator(*_pieceList); int t_c = 0; for(i = 0,savingIterator.toFirst();savingIterator.current() != NULL;++i,++savingIterator) { savingIterator.current()->save(f); if (_currentPieceData == savingIterator.current()) currentPieceNr = i; t_c++; } ASSERT(currentPieceNr != -1); *f << currentPieceNr; return true; } /* Load all data contained in this class */ bool CurrentData::load(QDataStream* f) { Q_INT32 c; Q_INT32 currentPieceNr; *f >> _score; *f >> _faults; *f >> _rounds; *f >> _elapsed; *f >> (Q_INT8&) _dirty; *f >> c; /* Number of pieces */ _pieceList = new QPtrList; for(int i = 1;i <= c;i++) { CPiece* p = new CPiece(_owner,f); _pieceList->append(p); } *f >> currentPieceNr; _pieceListIterator = new QPtrListIterator(*_pieceList); _pieceListIterator->toFirst(); *_pieceListIterator += currentPieceNr; _currentPieceData = _pieceListIterator->current(); return true; } /* Constructor. */ KPuzzleGame::KPuzzleGame(KPuzzleView* view) { _mainWidget = view; _status = GS_NOT_STARTED; connect(this,SIGNAL(sigTerm(int)),this,SLOT(slotTerm(int))); connect(&_timer,SIGNAL(timeout()),this,SLOT(slotTimer())); _scoreData = new ScoreData; scoreData()->_owner = this; _gameData = new GameData; gameData()->_owner = this; _mainPixmap = NULL; _currentData = new CurrentData; currentData()->_owner = this; } /* Initialization (parts which must be done outside the * constructor). Common init function called by initializeNew/ * initializeLoad. */ bool KPuzzleGame::initialize() { _picview = mainWidget()->picview(); _fullview = mainWidget()->fullview(); maskCache->setAutoDelete(true); maskCache->clear(); return true; } /* Initialization (parts which must be done outside the * constructor). This init function is called by * KPuzzleView::slotNewGame when a *new* game is created, */ bool KPuzzleGame::initializeNew() { if (!initialize()) return false; gameData()->_savePixmap = false; gameFilename() = QString::null; calcPiecesCount(); if (scalePixmap()) { gameData()->_piecesCount.setWidth(pixmapSize().width() / pieceSize().width()); gameData()->_piecesCount.setHeight(pixmapSize().height() / pieceSize().height()); } _gamePixmap = new QPixmap; _gamePixmap->resize(pixmapSize()); _gamePixmap->fill((QColor)black); ASSERT(piecesCount().width() >= 3); ASSERT(piecesCount().height() >= 3); currentData()->_pieceList = new QPtrList; currentData()->_pieceList->setAutoDelete(true); minglePieces(); currentData()->_pieceListIterator = new QPtrListIterator(*pieceList()); currentData()->_currentPieceData = currentData()->_pieceListIterator->toFirst(); currentData()->_currentPiecePixmap = new QPixmap(pieceSizeDisp()); getPiecePixmap(); /* Connect piece timer, if we are in the PIECE_TIME_GAME or UNBEARABLE_GAME * mode. */ connect(currentData()->_currentPieceData,SIGNAL(sigTimerTick(int)), this,SLOT(slotPieceTimer(int))); _picview->updatePixmap(gamePixmap(),gamePixmap()->rect()); _fullview->updatePixmap(mainPixmap(),mainPixmap()->rect()); setScore(0); currentData()->_elapsed = 0; currentData()->_dirty = false; initGameData(); mainWidget()->updateButtons(false,false, pixmapSize().width() > mainWidget()->gameWidget()->width(), pixmapSize().height() > mainWidget()->gameWidget()->height()); mainWidget()->gameWidget()->initialize(pixmapSize()); if (gameType() != PIECE_TIME_GAME && gameType() != UNBEARABLE_GAME) { char timeStr[6]; snprintf(timeStr,5,"%i:%i",scoreData()->_maxTime / 60,scoreData()->_maxTime % 60); mainWidget()->time()->display(timeStr); } startTimer(); setStatus(GS_RUNNING); updateAll(); mainWidget()->update(); currentData()->_running = true; currentPieceData()->showPiece(); return true; } /* Initialization (parts which must be done outside the * constructor). This init function is called by * KPuzzleView::slotOpenGame when a game is opened. */ bool KPuzzleGame::initializeLoad() { if (!initialize()) return false; if (!gameData()->_savePixmap) scalePixmap(); _gamePixmap = new QPixmap(pixmapSize()); _gamePixmap->fill((QColor)black); currentData()->_pieceList->setAutoDelete(true); currentData()->_currentPiecePixmap = new QPixmap(pieceSizeDisp()); getPiecePixmap(); /* Connect piece timer, if we are in the PIECE_TIME_GAME or UNBEARABLE_GAME * mode. */ connect(currentData()->_currentPieceData,SIGNAL(sigTimerTick(int)), this,SLOT(slotPieceTimer(int))); QPixmap piecePxm(currentPiecePixmap()->size()); #define CUR (drawingIterator.current()) QPtrListIterator drawingIterator(*pieceList()); for (drawingIterator.toFirst();drawingIterator.current() != NULL;++drawingIterator) if (CUR->hasBeenSet()) { getPiecePixmap(CUR,&piecePxm); QPoint tpos(CUR->pos().x() * pieceSize().width(),CUR->pos().y() * pieceSize().height()); if (maskedPieces()) tpos -= QPoint(displace(),displace()); bitBlt(gamePixmap(),tpos,&piecePxm,QRect(QPoint(0,0),pieceSizeDisp())); } #undef CUR _picview->updatePixmap(gamePixmap(),gamePixmap()->rect()); _fullview->updatePixmap(mainPixmap(),mainPixmap()->rect()); setScore(currentData()->_score); initGameData(); mainWidget()->updateButtons(false,false, pixmapSize().width() > mainWidget()->gameWidget()->width(), pixmapSize().height() > mainWidget()->gameWidget()->height()); mainWidget()->gameWidget()->initialize(pixmapSize()); if (gameType() != PIECE_TIME_GAME && gameType() != UNBEARABLE_GAME) { int rest = scoreData()->_maxTime - currentData()->_elapsed; QString s; s.sprintf("%i:%2i",rest / 60,rest % 60); mainWidget()->time()->display(s); } startTimer(); setStatus(GS_RUNNING); updateAll(); mainWidget()->update(); currentData()->_running = true; currentPieceData()->showPiece(); return true; } /* Called from the destructor before the object is destroyed */ void KPuzzleGame::done() { _picview = NULL; _fullview = NULL; ASSERT(currentData()->_pieceList != NULL); delete currentData()->_pieceListIterator; /* NB: The CPiece objects are deleted automatically when * they are removed from the list. */ while (!currentData()->_pieceList->isEmpty()) currentData()->_pieceList->removeFirst(); delete currentData()->_pieceList; if (currentData()->_currentPiecePixmap) delete currentData()->_currentPiecePixmap; delete _mainPixmap; _mainPixmap = NULL; delete _gamePixmap; _gamePixmap = NULL; } /* Destructor. Calls done(). */ KPuzzleGame::~KPuzzleGame() { if (status() != GS_NOT_STARTED) done(); delete _scoreData; delete _gameData; delete _currentData; } /* Called by the KPuzzleView object, calls initializeNew. */ bool KPuzzleGame::createNewGame() { QString fname = KFileDialog::getOpenFileName(LEVELS_DIR,"*.bmp *.jpg",mainWidget(),i18n("Open Image")); if (fname.isNull()) { return false; } CGameDialog gd(mainWidget()); if (gd.exec() != QDialog::Accepted) { return false; } gameData()->_filename = fname; gameData()->_dialog_pieceSize = gd.pieceSize(); gameData()->_gameType = gd.gameType(); gameData()->_difficulty = gd.difficulty(); gameData()->_scale = true; gameData()->_useMask = gd.useMask(); _mainPixmap = new QPixmap; _mainPixmap->load(gameData()->_filename); if (_mainPixmap->isNull()) { delete _mainPixmap; return false; } return true; } /* Called by the KPuzzleView object, calls initializeLoad. */ bool KPuzzleGame::createLoadedGame() { _gameFilename = KFileDialog::getOpenFileName(QDir::homeDirPath(),"*.pzz",mainWidget(),i18n("Open Game")); if (gameFilename().isNull()) { return false; } if (gameFilename().findRev(".") <= gameFilename().findRev("/")) // There is no dot after the last slash _gameFilename += ".pzz"; if (!QFile::exists(gameFilename())) { KMessageBox::sorry(0,i18n("The file you specified does not exist"),i18n("File does not exist")); return false; } QFile file(gameFilename()); if (!file.open(IO_ReadOnly)) { KMessageBox::sorry(0,i18n("The file you specified could not be opened for reading. Check the file's permissions."), i18n("File could not be opened")); return false; } QDataStream stream(&file); if (!load(&stream)) return false; return true; } /* Slot for the "Save" menu entry. Will call slotSaveGameAs * if this game has never been saved before (i.e. there is no * default filename). */ void KPuzzleGame::slotSaveGame() { bool gameIsPaused = paused(); pause(true); if (gameFilename().isNull()) { gameFilename() = KFileDialog::getSaveFileName(QDir::homeDirPath(),"*.pzz",mainWidget(),i18n("Save Game")); if (gameFilename().isNull()) { pause(gameIsPaused); return; } } CSaveDialog sd(mainWidget(),gameData()->_savePixmap); if (sd.exec() == QDialog::Rejected) { pause(gameIsPaused); return; } gameData()->_savePixmap = sd.savePixmap(); gameData()->_annotation = sd.annotation(); ASSERT(!gameFilename().isNull()); if (gameFilename().findRev(".") <= gameFilename().findRev("/")) /* There is no dot behind the last slash */ gameFilename() += ".pzz"; QFile file(gameFilename()); if (!file.open(IO_WriteOnly)) { KMessageBox::sorry(0,i18n("The file you specified could not be opened for writing. Check the file's" "permissions."),i18n("File could not be opened")); pause(gameIsPaused); return; } QDataStream stream(&file); save(&stream); pause(gameIsPaused); } /* Slot for the "Save As" menu entry. The player will be prompted for * a filename to save to. */ void KPuzzleGame::slotSaveGameAs() { bool gameIsPaused = paused(); pause(true); QString fname = KFileDialog::getSaveFileName(QDir::homeDirPath(),"*.pzz",mainWidget(),"Save Game"); if (fname.isNull()) { pause(gameIsPaused); return; } CSaveDialog sd(mainWidget(),gameData()->_savePixmap); if (sd.exec() == QDialog::Rejected) { pause(gameIsPaused); return; } gameData()->_savePixmap = sd.savePixmap(); gameData()->_annotation = sd.annotation(); gameFilename() = fname; if (gameFilename().findRev(".") <= gameFilename().findRev("/")) /* There is no dot behind the last slash */ gameFilename() += ".pzz"; QFile file(gameFilename()); if (!file.open(IO_WriteOnly)) { KMessageBox::sorry(0,i18n("The file you specified could not be opened for writing. Check the file's" "permissions."),i18n("File could not be opened")); pause(gameIsPaused); return; } QDataStream stream(&file); save(&stream); pause(gameIsPaused); } /* Slot for the "Show large image" menu entry. */ void KPuzzleGame::slotShowLarge() { if (status() != GS_RUNNING) return; pause(true); mainWidget()->setLargePxm(new QPixmap(getLargePixmap(mainWidget()->size()))); setStatus(GS_SHOW_LARGE); emit sigUpdate(); } /* Slot for the "Show full image" menu entry. */ void KPuzzleGame::slotShowMainPixmap() { if (status() != GS_RUNNING) return; pause(true); mainWidget()->setLargePxm(new QPixmap(getLargeMainPixmap(mainWidget()->size()))); setStatus(GS_SHOW_LARGE); emit sigUpdate(); } /* Slot for the "Pause" menu entry. */ void KPuzzleGame::slotPause() { pause(true); } /* Initializes all game data which must not be saved */ void KPuzzleGame::initGameData() { int tt = piecesTotal(); int r; /* score for placing a piece correctly */ scoreData()->_maxFaults = -1; scoreData()->_maxPieceTime = -1; scoreData()->_maxRounds = -1; switch (gameData()->_difficulty) { case 0: /* easy */ r = 50; scoreData()->_maxTime = tt * 60; scoreData()->_score1Time = tt * 10; scoreData()->_score2Time = tt * 20; scoreData()->_score3Time = tt * 30; scoreData()->_timeScore1 = tt * r / 10; scoreData()->_timeScore2 = tt * r / 15; scoreData()->_timeScore3 = tt * r / 20; scoreData()->_rightPieceScore = r; scoreData()->_wrongPieceScore = 0; switch (gameType()) { case STD_GAME: scoreData()->_bonus = 0; break; case PIECE_TIME_GAME: scoreData()->_bonus = tt * r / 15; scoreData()->_maxPieceTime = 60; break; case PIECE_FAULTS_GAME: scoreData()->_bonus = tt * r / 20; scoreData()->_maxFaults = 10; break; case FAULTS_SUM_GAME: scoreData()->_bonus = tt * r / 20; scoreData()->_maxFaults = 9 * tt; currentData()->_faults = 0; break; case UNBEARABLE_GAME: scoreData()->_bonus = tt * r / 10; scoreData()->_maxPieceTime = 15; scoreData()->_maxRounds = 5; currentData()->_rounds = 0; break; } break; case 1: /* medium */ r = 70; scoreData()->_maxTime = tt * 50; scoreData()->_score1Time = tt * 15; scoreData()->_score2Time = tt * 25; scoreData()->_score3Time = tt * 35; scoreData()->_timeScore1 = tt * r / 10; scoreData()->_timeScore2 = tt * r / 15; scoreData()->_timeScore3 = tt * r / 20; scoreData()->_rightPieceScore = r; scoreData()->_wrongPieceScore = 10; switch (gameType()) { case STD_GAME: scoreData()->_bonus = tt * r / 20; break; case PIECE_TIME_GAME: scoreData()->_bonus = tt * r / 12; scoreData()->_maxPieceTime = 50; break; case PIECE_FAULTS_GAME: scoreData()->_bonus = tt * r / 16; scoreData()->_maxFaults = 8; break; case FAULTS_SUM_GAME: scoreData()->_bonus = tt * r / 16; scoreData()->_maxFaults = 7 * tt; currentData()->_faults = 0; break; case UNBEARABLE_GAME: scoreData()->_bonus = tt * r / 6; scoreData()->_maxPieceTime = 12; scoreData()->_maxRounds = 4; currentData()->_rounds = 0; break; } break; case 2: /* hard */ r = 90; scoreData()->_maxTime = tt * 40; scoreData()->_score1Time = tt * 20; scoreData()->_score2Time = tt * 30; scoreData()->_score3Time = tt * 40; scoreData()->_timeScore1 = tt * r / 5; scoreData()->_timeScore2 = tt * r / 10; scoreData()->_timeScore3 = tt * r / 15; scoreData()->_rightPieceScore = r; scoreData()->_wrongPieceScore = 20; switch (gameType()) { case STD_GAME: scoreData()->_bonus = tt * r / 15; break; case PIECE_TIME_GAME: scoreData()->_bonus = tt * r / 10; scoreData()->_maxPieceTime = 40; break; case PIECE_FAULTS_GAME: scoreData()->_bonus = tt * r / 12; scoreData()->_maxFaults = 6; break; case FAULTS_SUM_GAME: scoreData()->_bonus = tt * r / 12; scoreData()->_maxFaults = 5 * tt; currentData()->_faults = 0; break; case UNBEARABLE_GAME: scoreData()->_bonus = tt * r / 3; scoreData()->_maxPieceTime = 10; scoreData()->_maxRounds = 3; currentData()->_rounds = 0; break; } break; } } /* Calculate the number of pieces in the game. Called by initializeNew. */ void KPuzzleGame::calcPiecesCount() { int tcx = 0; int tcy = 0; switch(gameData()->_dialog_pieceSize) { case 0: tcx = 88; tcy = 66; gameData()->_displace = maskedPieces() ? 19 : 0; break; case 1: tcx = 100; tcy = 75; gameData()->_displace = maskedPieces() ? 21 : 0; break; case 2: tcx = 112; tcy = 84; gameData()->_displace = maskedPieces() ? 24 : 0; break; case 3: tcx = 132; tcy = 99; gameData()->_displace = maskedPieces() ? 28 : 0; break; case 4: tcx = 140; tcy = 105; gameData()->_displace = maskedPieces() ? 30 : 0; break; } gameData()->_piecesCount = QSize(pixmapSize().width() / tcx,pixmapSize().height() / tcy); gameData()->_pieceSize = QSize(tcx,tcy); } /* Scale the main pixmap, according to the size of the pieces. * Returns true if the size of the pixmap has changed. */ bool KPuzzleGame::scalePixmap() { gameData()->_originalPixmapSize = pixmapSize(); if (pixmapSize().width() % pieceSize().width() || pixmapSize().height() % pieceSize().height()) { QWMatrix m; float scaleValX = (float) ((pixmapSize().width() / pieceSize().width() + 1) * pieceSize().width()) / pixmapSize().width(); float scaleValY = (float) ((pixmapSize().height() / pieceSize().height() + 1) * pieceSize().height()) / pixmapSize().height(); m.scale(scaleValX,scaleValY); QPixmap* pxm = new QPixmap(_mainPixmap->xForm(m)); delete _mainPixmap; _mainPixmap = pxm; return true; } return false; } /* Switch to next piece. The information about the current * piece (turned in which direction, elapsed time for this piece) * will be preserved. */ bool KPuzzleGame::setNextPiece() { CPiece* mark = currentPieceData(); mark->hidePiece(); disconnect(mark,SIGNAL(sigTimerTick(int)),this,SLOT(slotPieceTimer(int))); do { if (++(*pieceListIterator()) == NULL) { pieceListIterator()->toFirst(); if (gameType() == UNBEARABLE_GAME) { currentData()->_rounds++; if (currentData()->_rounds > scoreData()->_maxRounds) { emit sigTerm(TM_LOST); return false; } } } if (pieceListIterator()->current()->showPiece()) { currentData()->_currentPieceData = pieceListIterator()->current(); getPiecePixmap(); updateAll(); /* Update time widget */ if (gameType() == PIECE_TIME_GAME || gameType() == UNBEARABLE_GAME) slotPieceTimer(pieceListIterator()->current()->time()); /* Connect piece timer, if we are in the PIECE_TIME_GAME or UNBEARABLE_GAME * mode. */ connect(currentData()->_currentPieceData,SIGNAL(sigTimerTick(int)), this,SLOT(slotPieceTimer(int))); return true; } } while (pieceListIterator()->current() != mark); /* This only happens if there is no piece to be shown. */ if (currentData()->_dirty) { /* There are pieces left, but the time for them * has run out. */ emit sigTerm(TM_LOST); return false; } else { /* All pieces are in their places. */ emit sigTerm(TM_WON); return false; } } /* Switch to previous piece. */ bool KPuzzleGame::setPrevPiece() { CPiece* mark = currentPieceData(); mark->hidePiece(); do { if (--(*pieceListIterator()) == NULL) pieceListIterator()->toLast(); if (pieceListIterator()->current()->showPiece()) { currentData()->_currentPieceData = pieceListIterator()->current(); getPiecePixmap(); updateAll(); /* Update time widget */ if (gameType() == PIECE_TIME_GAME || gameType() == UNBEARABLE_GAME) slotPieceTimer(pieceListIterator()->current()->time()); /* Connect piece timer, if we are in the PIECE_TIME_GAME or UNBEARABLE_GAME * mode. */ connect(currentData()->_currentPieceData,SIGNAL(sigTimerTick(int)), this,SLOT(slotPieceTimer(int))); return true; } } while (pieceListIterator()->current() != mark); if (currentData()->_dirty) { emit sigTerm(TM_LOST); return false; } else { emit sigTerm(TM_WON); return false; } } /* Try to place a piece at the current position. If sucessful, * return true. This function seems to be able to terminate the * game. */ bool KPuzzleGame::setPiece(QPoint pos) { if (currentPieceData()->setPiece(pos)) { /* The piece has been placed correctly */ changeScore(SC_PIECE_RIGHT); QPoint tpos(pos.x() * pieceSize().width(),pos.y() * pieceSize().height()); if (maskedPieces()) { tpos -= QPoint(displace(),displace()); bitBlt(gamePixmap(),tpos,currentPiecePixmap(),QRect(QPoint(0,0),pieceSizeDisp())); } else { bitBlt(gamePixmap(),tpos,currentPiecePixmap(),QRect(QPoint(0,0),pieceSize())); } _picview->addPiece(currentPiecePixmap(),currentPieceData()->pos()); setNextPiece(); return true; } else { /* This is the wrong place! */ changeScore(SC_PIECE_WRONG); if (gameType() == FAULTS_SUM_GAME) { currentData()->_faults++; if (currentData()->_faults > scoreData()->_maxFaults) { emit sigTerm(TM_LOST); return false; } } if (!currentPieceData()->mayKeepPiece()) { currentData()->_dirty = true; setNextPiece(); } return false; } } /* Called by CPiece when the time allowed for one piece * has run out. */ void KPuzzleGame::hidePiece() { switch(gameType()) { case PIECE_TIME_GAME: currentData()->_dirty = true; /* This game cannot be won any more */ break; case UNBEARABLE_GAME: break; default: break; } setNextPiece(); } /* Mingle pieces, create the CPiece objects and insert them into the * _pieceList. */ void KPuzzleGame::minglePieces() { ASSERT(pieceList()->isEmpty()); for (int j = 0;j < piecesCount().height();j++) for (int i = 0;i < piecesCount().width();i++) { CPiece* p = new CPiece(this,QPoint(i,j));; p->turn() = rand() % 4; currentData()->_pieceList->insert(rand() % (j * piecesCount().width() + i + 1),p); } } /* Writes pixmap of piece pc or of the current piece to pxm * or _currentData->_currentPiecePixmap. */ void KPuzzleGame::getPiecePixmap(CPiece* pc,QPixmap* pxm) const { if (pc) pc->getPixmap((QPixmap*) (pxm ? pxm : currentPiecePixmap())); else currentPieceData()->getPixmap((QPixmap*) (pxm ? pxm : currentPiecePixmap())); } /* The current piece pixmap, turned in one of the four possible * directions. The current direction is saved along with each piece, */ QPixmap* KPuzzleGame::currentPieceTurned() const { QPixmap* ret = new QPixmap; currentPieceData()->getTurnedPixmap(ret); return ret; } /* Resize the game pixmap (the one which the player is * trying to put together) so that it has got the size * of the game window, and return the resized pixmap. */ QPixmap KPuzzleGame::getLargePixmap(QSize size) const { float scaleX = (float) size.width() / gamePixmap()->width(); // With this factor, the width of // the scaled picture is equal to the width of the window float scaleY = (float) size.height() / gamePixmap()->height(); // same with height QWMatrix m; m.scale(min(scaleX,scaleY),min(scaleX,scaleY)); QPixmap pxm = gamePixmap()->xForm(m); return pxm; } /* Resize the main pixmap (the one which is * complete) so that it has got the size * of the game window, and return the resized pixmap. */ QPixmap KPuzzleGame::getLargeMainPixmap(QSize size) const { float scaleX = (float) size.width() / gamePixmap()->width(); /* With this factor, the width of * the scaled picture is equal to the width of the window */ float scaleY = (float) size.height() / gamePixmap()->height(); /* same with height */ QWMatrix m; m.scale(min(scaleX,scaleY),min(scaleX,scaleY)); QPixmap pxm = mainPixmap()->xForm(m); return pxm; } /* Update the entire widget. */ void KPuzzleGame::updateAll() { mainWidget()->updateAll(); } /* Change the score of the player. The reason is one of the SC_XXX * constants defined in kpuzzlegame.cpp. */ void KPuzzleGame::changeScore(char reason) { int add = 0; switch(reason) { case SC_PIECE_RIGHT: setScore(score() + scoreData()->_rightPieceScore); break; case SC_PIECE_WRONG: if (score() > 0) setScore(score() > scoreData()->_wrongPieceScore ? score() - scoreData()->_wrongPieceScore : 1); break; case SC_COUNT_TIME: if (elapsed() < scoreData()->_score3Time) add = scoreData()->_timeScore3; if (elapsed() < scoreData()->_score2Time) add = scoreData()->_timeScore2; if (elapsed() < scoreData()->_score1Time) add = scoreData()->_timeScore1; setScore(score() + add); break; case SC_BONUS: setScore(score() + scoreData()->_bonus); break; } } /* Show the highscore dialog. */ void KPuzzleGame::showHighscoreDialog() const { CHighscoreDialog hs(mainWidget()->parentWidget(),score()); hs.initializeDialog(); hs.exec(); } /* Save this game to a file */ bool KPuzzleGame::save(QDataStream* f) const { if (!gameData()->save(f)) return false; if (gameData()->_savePixmap) { *f << *mainPixmap(); } else { } if (!currentData()->save(f)) return false; return true; } /* Load a game from a file (must be called by createLoadedGame). */ bool KPuzzleGame::load(QDataStream* f) { GameData* newGD = new GameData; newGD->_owner = this; QPixmap* newMainPixmap; if (!newGD->load(f)) { /* This loads the filename of the main pixmap */ delete newGD; return false; } newMainPixmap = new QPixmap; /* This asserts that we have got the right pixmap */ QSize gpSize; if (newGD->_savePixmap) { *f >> *newMainPixmap; } else { gpSize = newGD->_originalPixmapSize; ASSERT(!(newGD->_filename.isNull())); QString base(newGD->_filename.right(newGD->_filename.length() - (newGD->_filename.findRev('/') + 1))); QString dir(newGD->_filename.left(newGD->_filename.findRev('/') + 1)); /* If the pixmap is no more found in the place it was * found when the game was saved... */ if (!(QFile::exists(dir + base) && newMainPixmap->load(dir + base))) /* we look into the standard place */ if (!(QFile::exists(LEVELS_DIR + base) && newMainPixmap->load(LEVELS_DIR + base))) if (!(QFile::exists(QDir::homeDirPath() + base) && newMainPixmap->load(QDir::homeDirPath() + base))) { QString sorryMsg(i18n("Sorry, the picture for the" " puzzle game you selected could not be found.\n" "Please select a picture you want to use instead.\n" "Make sure it has the size of %1 x %2 pixels.")); sorryMsg = sorryMsg.arg(gpSize.width()).arg(gpSize.height()); KMessageBox::sorry(mainWidget(),sorryMsg); while (true) { QString newFileName = KFileDialog::getOpenFileName(LEVELS_DIR, "*.bmp",0,i18n("Choose picture")); if (newFileName.isNull()) { /* user hit cancel */ delete newGD; delete newMainPixmap; return false; } if (!(QFile::exists(newFileName) && newMainPixmap->load(newFileName))) { KMessageBox::sorry(mainWidget(), i18n("Your picture could not be loaded. Try again.")); } else { if (newMainPixmap->size() != gpSize) { KMessageBox::sorry(mainWidget(), i18n("Your picture has the wrong size. Try again.")); } else { /* Everything OK */ break; } } } } } CurrentData* newCD = new CurrentData; newCD->_owner = this; if (!newCD->load(f)) { delete newGD; delete newMainPixmap; return false; } if (_mainPixmap) delete _mainPixmap; delete _gameData; delete _currentData; _mainPixmap = newMainPixmap; _gameData = newGD; _currentData = newCD; return true; } /* Set the status of the game (one of the GS_XXX variables defined * above). Enable/disable menu entries accordingly. Show * a status line text, if necessary. */ void KPuzzleGame::setStatus(int s) { int enable = MI_NONE; _status = s; mainWidget()->hideStatusMsg(); switch(s) { case GS_NOT_STARTED: enable = MI_NONE; mainWidget()->showBackground(false); done(); emit sigDone(); return; case GS_RUNNING: enable = MI_STOP_GAME | MI_PAUSE | MI_SHOW_LARGE | MI_SAVE_GAME; mainWidget()->showBackground(true); mainWidget()->freeGamePixmap(GP_BIGLOGO); mainWidget()->freeGamePixmap(GP_PAUSE); break; case GS_WINNING: enable = MI_STOP_GAME; mainWidget()->showBackground(false); mainWidget()->showStatusMsg(i18n("Press mouse key to continue")); mainWidget()->showAll(false); mainWidget()->useGamePixmap(GP_WIN); mainWidget()->setLargePxm(NULL); break; case GS_SHOW_LARGE: enable = MI_STOP_GAME | MI_SAVE_GAME; mainWidget()->showBackground(false); mainWidget()->showStatusMsg(i18n("Press mouse key to continue")); break; case GS_SHOW_LARGE_WINNING: enable = MI_STOP_GAME; mainWidget()->showBackground(false); mainWidget()->showStatusMsg(i18n("Press mouse key to continue")); mainWidget()->setLargePxm(new QPixmap(getLargePixmap(mainWidget()->size()))); mainWidget()->freeGamePixmap(GP_WIN); break; case GS_LOSING: enable = MI_STOP_GAME; mainWidget()->showBackground(false); mainWidget()->showStatusMsg(i18n("Press mouse key to continue")); mainWidget()->showAll(false); mainWidget()->useGamePixmap(GP_LOSE); mainWidget()->setLargePxm(NULL); break; case GS_SHOW_LARGE_LOSING: enable = MI_STOP_GAME; mainWidget()->showBackground(false); mainWidget()->showStatusMsg(i18n("Press mouse key to continue")); mainWidget()->setLargePxm(new QPixmap(getLargePixmap(mainWidget()->size()))); mainWidget()->freeGamePixmap(GP_LOSE); break; case GS_SHOW_HIGHSCORES: enable = MI_NONE; mainWidget()->showBackground(false); showHighscoreDialog(); setStatus(GS_NOT_STARTED); return; case GS_PAUSED: enable = MI_STOP_GAME | MI_SAVE_GAME; mainWidget()->showBackground(false); mainWidget()->showStatusMsg(i18n("Press mouse key to continue")); mainWidget()->useGamePixmap(GP_PAUSE); break; } mainWidget()->enableMenuItems(enable); } /* Pause the game if p is true. Resume the game if * p is false. */ void KPuzzleGame::pause(bool p) { if (status() == GS_NOT_STARTED) return; if (p) { if (status() == GS_PAUSED) return; stopTimer(); currentPieceData()->stopTimer(); setStatus(GS_PAUSED); mainWidget()->showAll(false); mainWidget()->update(); } else { if (status() == GS_RUNNING) return; startTimer(); currentPieceData()->startTimer(); setStatus(GS_RUNNING); mainWidget()->showAll(true); mainWidget()->update(); } } /* Slot for the timer of the current piece (necessary for * updating the time counter widget). */ void KPuzzleGame::slotPieceTimer(int sec) { QString s; s.sprintf("%i:%2i",sec / 60,sec % 60); mainWidget()->time()->display(s); } /* Slot for the global game timer. */ void KPuzzleGame::slotTimer() { currentData()->_elapsed++; int rest = scoreData()->_maxTime - currentData()->_elapsed; QString s; /* If we have PIECE_TIME_GAME or UNBEARABLE_GAME, the time * widget will not show the "global" time, but the time * for the current piece. */ if (gameType() != PIECE_TIME_GAME && gameType() != UNBEARABLE_GAME) { s.sprintf("%i:%2i",rest / 60,rest % 60); mainWidget()->time()->display(s); } if (rest < 0) emit sigTerm(TM_LOST); } /* Slot to terminate the game. */ void KPuzzleGame::slotTerm(int reason) { stopTimer(); currentPieceData()->stopTimer(); switch (reason) { case TM_WON: changeScore(SC_COUNT_TIME); changeScore(SC_BONUS); setStatus(GS_WINNING); mainWidget()->update(); break; case TM_LOST: setStatus(GS_LOSING); mainWidget()->update(); break; case TM_QUIT: setStatus(GS_LOSING); mainWidget()->update(); break; /* If reason is TM_FORCE, _status should be GS_NOT_STARTED when leaving, i.e. * the game should be in its initial state. */ case TM_FORCE: switch (status()) { case GS_RUNNING: case GS_PAUSED: case GS_SHOW_LARGE: case GS_SHOW_LARGE_WINNING: case GS_SHOW_LARGE_LOSING: break; case GS_WINNING: case GS_LOSING: break; } setStatus(GS_NOT_STARTED); return; } }