diff --git a/ai_kepler.h b/ai_kepler.h index 9034fff..e5f0d38 100644 --- a/ai_kepler.h +++ b/ai_kepler.h @@ -1,66 +1,66 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer Copyright (C) 2012 by Ian Wadham 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. **************************************************************************** */ #ifndef AI_KEPLER_H #define AI_KEPLER_H #include "ai_base.h" /** * Class AI_Kepler computes the value of moving a cube. It assists the main AI * class and contains the algorithm from the original KJumpingCube code. * * @short The Kepler AI algorithm */ class AI_Kepler : public AI_Base { public: - QString whoami() override { return QString ("Kepler"); } // IDW test. + QString whoami() override { return QStringLiteral ("Kepler"); } // IDW test. /** * The Kepler AI constructor. */ AI_Kepler(); /** * Assess the priority of playing a cube at a particular position. The * highest priority cubes are used by the AI_Main class for look-ahead moves * and calculating the values of the positions reached. The cube to be * assessed has to be neutral or owned by the player who is to move. * * @param index The index-position of the cube to be assessed * @param player The player who is to move * @param neighbors The index-positions of the cube's neighbors (array), * where a value of -1 means no neighbor on that side * @param owners The current owners of the cubes in the box (array) * @param values The current point values of the cubes in the box (array) * @param maxValues The maximum point values of the cubes in the box (array) * * @return The priority of a move (always > 0): moves with priority * 1 are best and those with priority >= HighValue (999) are * worst but may be forced (e.g. when defeat is imminent). */ int assessCube (const int index, const Player player, const int neighbors [4], const Player owners[], const int values[], const int maxValues[]) const override; }; #endif // AI_KEPLER_H diff --git a/ai_newton.h b/ai_newton.h index 744ffcf..b90548b 100644 --- a/ai_newton.h +++ b/ai_newton.h @@ -1,66 +1,66 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer Copyright (C) 2012 by Ian Wadham 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. **************************************************************************** */ #ifndef AI_NEWTON_H #define AI_NEWTON_H #include "ai_base.h" /** * Class AI_Newton computes the priority of moving a cube and the value of the * resulting position. It assists the main AI class. * * @short The Newton AI algorithms */ class AI_Newton : public AI_Base { public: - QString whoami() override { return QString ("Newton"); } // IDW test. + QString whoami() override { return QStringLiteral ("Newton"); } // IDW test. /** * The Newton AI constructor. */ AI_Newton(); /** * Assess the priority of playing a cube at a particular position. The * highest priority cubes are used by the AI_Main class for look-ahead moves * and calculating the values of the positions reached. The cube to be * assessed has to be neutral or owned by the player who is to move. * * @param index The index-position of the cube to be assessed * @param player The player who is to move * @param neighbors The index-positions of the cube's neighbors (array), * where a value of -1 means no neighbor on that side * @param owners The current owners of the cubes in the box (array) * @param values The current point values of the cubes in the box (array) * @param maxValues The maximum point values of the cubes in the box (array) * * @return The priority of a move (always > 0): moves with priority * 1 are best and those with priority >= HighValue (999) are * worst but may be forced (e.g. when defeat is imminent). */ int assessCube (const int index, const Player player, const int neighbors [4], const Player owners[], const int values[], const int maxValues[]) const override; }; #endif // AI_NEWTON_H diff --git a/game.cpp b/game.cpp index f2c187a..4625cb0 100644 --- a/game.cpp +++ b/game.cpp @@ -1,976 +1,976 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer Copyright (C) 2012-2013 by Ian Wadham 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 "game.h" #include "version.h" #include "ai_main.h" #include "ai_box.h" #include "kcubeboxwidget.h" #include "settingswidget.h" #include "kjumpingcube_debug.h" #include #include #include // IDW test. #include #include #include #include #include #include "prefs.h" #define LARGE_NUMBER 999999 Game::Game (const int d, KCubeBoxWidget * view, QWidget * parent) : QObject ((QObject *) parent), // Delete Game when window is deleted. m_activity (Idle), m_waitingState (Nil), m_waitingToMove (false), m_moveNo (0), // Game not started. m_endMoveNo (LARGE_NUMBER), // Game not finished. m_interrupting (false), m_newSettings (false), m_parent (parent), m_view (view), m_settingsPage (0), m_side (d), m_currentPlayer (One), m_index (0), m_fullSpeed (false), computerPlOne (false), computerPlTwo (false), m_pauseForComputer (false), m_pauseForStep (false) { qCDebug(KJUMPINGCUBE_LOG) << "CONSTRUCT Game: side" << m_side; m_box = new AI_Box (this, m_side); m_ai = new AI_Main (this, m_side); m_steps = new QList; connect(m_view, &KCubeBoxWidget::mouseClick, this, &Game::startHumanMove); connect(m_ai, &AI_Main::done, this, &Game::moveCalculationDone); connect(m_view, &KCubeBoxWidget::animationDone, this, &Game::animationDone); } Game::~Game() { if ((m_activity != Idle) && (m_activity != Aborting)) { shutdown(); } delete m_steps; } void Game::gameActions (const int action) { qCDebug(KJUMPINGCUBE_LOG) << "GAME ACTION IS" << action; if ((m_activity != Idle) && (action != BUTTON) && (action != NEW)) { m_view->showPopup (i18n("Sorry, doing a move...")); return; } switch (action) { case NEW: newGame(); break; case HINT: if (! isComputer (m_currentPlayer)) { KCubeWidget::enableClicks (false); computeMove(); } break; case BUTTON: buttonClick(); break; case UNDO: undo(); break; case REDO: redo(); break; case SAVE: case SAVE_AS: saveGame (action == SAVE_AS); break; case LOAD: loadGame(); break; default: break; } } void Game::showWinner() { emit buttonChange (false, false, i18n("Game over")); QString s = i18n("The winner is Player %1!", m_currentPlayer); KMessageBox::information (m_view, s, i18n("Winner")); } void Game::showSettingsDialog (bool show) { // Show the Preferences/Settings/Configuration dialog. - KConfigDialog * settings = KConfigDialog::exists ("settings"); + KConfigDialog * settings = KConfigDialog::exists (QStringLiteral("settings")); if (! settings) { - settings = new KConfigDialog (m_parent, "settings", Prefs::self()); + settings = new KConfigDialog (m_parent, QStringLiteral("settings"), Prefs::self()); settings->setFaceType (KPageDialog::Plain); SettingsWidget * widget = new SettingsWidget (m_parent); - settings->addPage (widget, i18n("General"), "games-config-options"); + settings->addPage (widget, i18n("General"), QStringLiteral("games-config-options")); connect(settings, &KConfigDialog::settingsChanged, this, &Game::newSettings); m_settingsPage = widget; // Used when reverting/editing settings. } if (! show) return; settings->show(); settings->raise(); // Force the dialog to be in front. } void Game::newSettings() { qCDebug(KJUMPINGCUBE_LOG) << "NEW SETTINGS" << m_newSettings << "m_activity" << m_activity << "size:" << Prefs::cubeDim() << "m_side" << m_side; loadImmediateSettings(); m_newSettings = true; if (m_side != Prefs::cubeDim()) { QMetaObject::invokeMethod (this, "newGame", Qt::QueuedConnection); return; } // Waiting to move and not Hinting? else if (m_waitingToMove && (m_activity == Idle)) { loadPlayerSettings(); m_newSettings = false; setUpNextTurn(); } // Else, the remaining settings will be loaded at the next start of a turn. // They set computer pause on/off and computer player 1 or 2 on/off. } void Game::loadImmediateSettings() { qCDebug(KJUMPINGCUBE_LOG) << "GAME LOAD IMMEDIATE SETTINGS entered"; // Color changes can take place as soon as control returns to the event loop. // Changes of animation type or speed will take effect next time there is an // animation step to do, regardless of current activity. bool reColorCubes = m_view->loadSettings(); m_fullSpeed = Prefs::animationNone(); m_pauseForStep = Prefs::pauseForStep(); if (reColorCubes) { emit playerChanged (m_currentPlayer); // Re-display status bar icon. } // Choices of computer AIs and skills will take effect next time there is a // computer move or hint. They will not affect any calculation in progress. m_ai->setSkill (Prefs::skill1(), Prefs::kepler1(), Prefs::newton1(), Prefs::skill2(), Prefs::kepler2(), Prefs::newton2()); qCDebug(KJUMPINGCUBE_LOG) << "m_pauseForStep" << m_pauseForStep; qCDebug(KJUMPINGCUBE_LOG) << "PLAYER 1 settings: skill" << Prefs::skill1() << "Kepler" << Prefs::kepler1() << "Newton" << Prefs::newton1(); qCDebug(KJUMPINGCUBE_LOG) << "PLAYER 2 settings: skill" << Prefs::skill2() << "Kepler" << Prefs::kepler2() << "Newton" << Prefs::newton2(); } void Game::loadPlayerSettings() { qCDebug(KJUMPINGCUBE_LOG) << "GAME LOAD PLAYER SETTINGS entered"; bool oldComputerPlayer = isComputer (m_currentPlayer); m_pauseForComputer = Prefs::pauseForComputer(); computerPlOne = Prefs::computerPlayer1(); computerPlTwo = Prefs::computerPlayer2(); qCDebug(KJUMPINGCUBE_LOG) << "AI 1" << computerPlOne << "AI 2" << computerPlTwo << "m_pauseForComputer" << m_pauseForComputer; if (isComputer (m_currentPlayer) && (! oldComputerPlayer)) { qCDebug(KJUMPINGCUBE_LOG) << "New computer player set: must wait."; m_waitingState = ComputerToMove; // New player: don't start playing yet. } } void Game::startHumanMove (int x, int y) { int index = x * m_side + y; bool humanPlayer = (! isComputer (m_currentPlayer)); qCDebug(KJUMPINGCUBE_LOG) << "CLICK" << x << y << "index" << index; if (! humanPlayer) { buttonClick(); } else if (humanPlayer && ((m_currentPlayer == m_box->owner(index)) || (m_box->owner(index) == Nobody))) { m_waitingToMove = false; m_moveNo++; m_endMoveNo = LARGE_NUMBER; qCDebug(KJUMPINGCUBE_LOG) << "doMove (" << index; KCubeWidget::enableClicks (false); doMove (index); } } void Game::setUpNextTurn() { // Called from newSettings(), new game, load, change player and undo/redo. if (m_newSettings) { m_newSettings = false; loadPlayerSettings(); } qCDebug(KJUMPINGCUBE_LOG) << "setUpNextTurn" << m_currentPlayer << computerPlOne << computerPlTwo << "pause" << m_pauseForComputer << "wait" << m_waitingState << "waiting" << m_waitingToMove; if (isComputer (m_currentPlayer)) { // A computer player is to move. qCDebug(KJUMPINGCUBE_LOG) << "(m_pauseForComputer || (m_waitingState == ComputerToMove))" << (m_pauseForComputer || (m_waitingState == ComputerToMove)); if (m_pauseForComputer || (m_waitingState == ComputerToMove) || (m_moveNo == 0)) { m_waitingState = ComputerToMove; m_waitingToMove = true; if (computerPlOne && computerPlTwo) { if (m_moveNo == 0) { emit buttonChange (true, false, i18n("Start game")); } else { emit buttonChange (true, false, i18n("Continue game")); } } else { emit buttonChange (true, false, i18n("Start computer move")); } // Wait for a button-click to show that the user is ready. qCDebug(KJUMPINGCUBE_LOG) << "COMPUTER MUST WAIT"; KCubeWidget::enableClicks (true); return; } // Start the computer's move. qCDebug(KJUMPINGCUBE_LOG) << "COMPUTER MUST MOVE"; m_waitingState = Nil; m_waitingToMove = false; KCubeWidget::enableClicks (false); computeMove(); } else { // A human player is to move. qCDebug(KJUMPINGCUBE_LOG) << "HUMAN TO MOVE"; KCubeWidget::enableClicks (true); m_waitingState = Nil; m_waitingToMove = true; if (computerPlOne || computerPlTwo) { emit buttonChange (false, false, i18n("Your turn")); } else { emit buttonChange (false, false, i18n("Player %1", m_currentPlayer)); } // Wait for a click on the cube to be moved. } } void Game::computeMove() { #if AILog > 1 t.start(); // Start timing the AI calculation. #endif m_view->setWaitCursor(); m_activity = Computing; setStopAction(); emit setAction (HINT, false); if (isComputer (m_currentPlayer)) { emit statusMessage (i18n("Computer player %1 is moving", m_currentPlayer), false); } m_ai->getMove (m_currentPlayer, m_box); } void Game::moveCalculationDone (int index) { // We do not care if we interrupted the computer. It was probably taking // too long, so we will use the best move it had so far. // We are starting a new game or closing KJumpingCube. See shutdown(). if (m_activity == Aborting) { m_activity = Idle; return; } m_activity = Idle; if ((index < 0) || (index >= (m_side * m_side))) { m_view->setNormalCursor(); KMessageBox::sorry (m_view, i18n ("The computer could not find a valid move.")); // IDW TODO - What to do about state values and BUTTON ??? return; } #if AILog > 1 qCDebug(KJUMPINGCUBE_LOG) << "TIME of MOVE" << t.elapsed(); qCDebug(KJUMPINGCUBE_LOG) << "=============================================================="; #endif // Blink the cube to be moved (twice). m_view->startAnimation (false, index); m_activity = ShowingMove; setStopAction(); } void Game::showingDone (int index) { if (isComputer (m_currentPlayer)) { m_moveNo++; m_endMoveNo = LARGE_NUMBER; qCDebug(KJUMPINGCUBE_LOG) << "m_moveNo" << m_moveNo << "isComputer()" << (isComputer (m_currentPlayer)); doMove (index); // Animate computer player's move. } else { moveDone(); // Finish Hint animation. setUpNextTurn(); // Wait: unless player setting changed. } } void Game::doMove (int index) { bool computerMove = ((computerPlOne && m_currentPlayer == One) || (computerPlTwo && m_currentPlayer == Two)); // Make a copy of the position and player to move for the Undo function. m_box->copyPosition (m_currentPlayer, computerMove, index); #if AILog > 0 if (! computerMove) { // Record a human move in the statistics. m_ai->postMove (m_currentPlayer, index, m_side); } #endif emit setAction (UNDO, true); // Update Undo and Redo actions. emit setAction (REDO, false); m_steps->clear(); bool won = m_box->doMove (m_currentPlayer, index, 0, m_steps); #if AILog > 1 qCDebug(KJUMPINGCUBE_LOG) << "GAME WON?" << won << "STEPS" << (* m_steps); // m_box->printBox(); #endif if (m_steps->count() > 1) { m_view->setWaitCursor(); //This will be a stoppable animation. } m_activity = AnimatingMove; doStep(); } void Game::doStep() { // Re-draw all cubes affected by a move, proceeding one step at a time. int index; bool startStep = true; do { if (! m_steps->isEmpty()) { index = m_steps->takeFirst(); // Get a cube to be re-drawn. // Check if the player wins at this step (no more cubes are re-drawn). if (index == 0) { moveDone(); m_endMoveNo = m_moveNo; #if AILog > 0 qCDebug(KJUMPINGCUBE_LOG) << "\nCALLING dumpStats()"; m_ai->dumpStats(); #endif showWinner(); return; } // Update the view of a cube, either immediately or via animation. startStep = (index > 0); // + -> increment, - -> expand. index = startStep ? (index - 1) : (-index - 1); int value = m_view->cubeValue (index); // Pre-update in view. int max = m_box->maxValue (index); if (startStep) { // Add 1 and take. m_view->displayCube (index, m_currentPlayer, value + 1); if ((value >= max) && (! m_fullSpeed)) { m_view->highlightCube (index, true); } } else if (m_fullSpeed) { // Decrease immediately. m_view->displayCube (index, m_currentPlayer, value - max); m_view->highlightCube (index, false); // Maybe user hit Stop. } else { // Animate cascade step. if (m_pauseForStep && (m_waitingState != WaitingToStep)) { // Pause: return the step to the list and wait for a buttonClick. m_steps->prepend (-index - 1); m_waitingState = WaitingToStep; emit buttonChange (true, false, i18n("Show next step")); return; } // Now set the button up and start the animation. setStopAction(); m_view->startAnimation (true, index); } } else { // Views of the cubes at all steps of the move have been updated. moveDone(); changePlayer(); return; } } while (startStep || m_fullSpeed); } void Game::stepAnimationDone (int index) { // Finished a move step. Decrease the cube that expanded and display it. int value = m_view->cubeValue (index); int max = m_box->maxValue (index); m_view->displayCube (index, m_currentPlayer, value - max); doStep(); // Do next animation step (if any). } void Game::moveDone() { // Called after non-animated move, animated move, end of game or hint action. m_view->setNormalCursor(); - emit statusMessage (QString(""), false); // Clear the status bar. + emit statusMessage (QLatin1String(""), false); // Clear the status bar. m_activity = Idle; setAction (HINT, true); m_fullSpeed = Prefs::animationNone(); m_view->hidePopup(); if (m_interrupting) { m_interrupting = false; m_waitingState = ComputerToMove; } } Player Game::changePlayer() { m_currentPlayer = (m_currentPlayer == One) ? Two : One; emit playerChanged (m_currentPlayer); setUpNextTurn(); return m_currentPlayer; } void Game::buttonClick() { qCDebug(KJUMPINGCUBE_LOG) << "BUTTON CLICK seen: waiting" << m_waitingToMove << "m_activity" << m_activity << "m_waitingState" << m_waitingState; if (m_waitingState == Nil) { // Button is red: stop an activity. if ((! m_pauseForComputer) && (! m_interrupting) && (computerPlOne && computerPlTwo)) { m_interrupting = true; // Interrupt a non-stop AI v AI game. m_view->showPopup (i18n("Finishing move...")); setStopAction(); // Change to text for current activity. } else if (m_activity == Computing) { m_ai->stop(); // Stop calculating a move or hint. m_activity = Stopping; } else if (m_activity == ShowingMove) { int index = m_view->killAnimation(); showingDone (index); // Stop showing where a move or hint is. m_view->highlightCube (index, false); } else if (m_activity == AnimatingMove) { int index = m_view->killAnimation(); m_fullSpeed = true; // Go to end of move right now, skipping stepAnimationDone (index); // all later steps of animation. } } else { // Button is green: start an activity. switch (m_waitingState) { case WaitingToStep: doStep(); // Start next animation step. break; case ComputerToMove: computeMove(); // Start next computer move. break; default: break; } m_waitingState = Nil; } } void Game::setStopAction() { // Red button setting for non-stop AI v. AI game. if ((! m_pauseForComputer) && (! m_interrupting) && (computerPlOne && computerPlTwo)) { if (m_activity == Computing) { // Starting AI v. AI move. emit buttonChange (true, true, i18n("Interrupt game")); } return; // Continuing AI v. AI move. } // Red button settings for AI v. human, two human players or pausing game. if (m_activity == Computing) { // Calculating hint or AI move. emit buttonChange (true, true, i18n("Stop computing")); } else if (m_activity == ShowingMove) { // Showing hint or AI move. emit buttonChange (true, true, i18n("Stop showing move")); } else if (m_activity == AnimatingMove) { // Animating AI or human move. emit buttonChange (true, true, i18n("Stop animation")); } } /* ************************************************************************** */ /* STANDARD GAME ACTIONS */ /* ************************************************************************** */ void Game::newGame() { qCDebug(KJUMPINGCUBE_LOG) << "NEW GAME entered: waiting" << m_waitingToMove << "won?" << (m_moveNo >= m_endMoveNo); if (newGameOK()) { qCDebug(KJUMPINGCUBE_LOG) << "QDEBUG: newGameOK() =" << true; shutdown(); // Stop the current move (if any). m_view->setNormalCursor(); m_view->hidePopup(); loadImmediateSettings(); loadPlayerSettings(); m_newSettings = false; qCDebug(KJUMPINGCUBE_LOG) << "newGame() loadSettings DONE: waiting" << m_waitingToMove << "won?" << (m_moveNo >= m_endMoveNo) << "move" << m_moveNo << m_endMoveNo; qCDebug(KJUMPINGCUBE_LOG) << "setDim (" << Prefs::cubeDim() << ") m_side" << m_side; setDim (Prefs::cubeDim()); qCDebug(KJUMPINGCUBE_LOG) << "Entering reset();"; reset(); // Clear cubebox, initialise states. emit setAction (UNDO, false); emit setAction (REDO, false); emit statusMessage (i18n("New Game"), false); m_moveNo = 0; m_endMoveNo = LARGE_NUMBER; setUpNextTurn(); } else qCDebug(KJUMPINGCUBE_LOG) << "QDEBUG: newGameOK() =" << false; } void Game::saveGame (bool saveAs) { if (saveAs || m_gameURL.isEmpty()) { int result=0; QUrl url; do { - url = QFileDialog::getSaveFileUrl (m_view, QString(), m_gameURL, "*.kjc"); + url = QFileDialog::getSaveFileUrl (m_view, QString(), m_gameURL, QStringLiteral("*.kjc")); if (url.isEmpty()) return; // check filename QRegExp pattern ("*.kjc", Qt::CaseSensitive, QRegExp::Wildcard); if (! pattern.exactMatch (url.fileName())) { url.setPath(url.path() +".kjc"); } KIO::StatJob* statJob = KIO::stat(url, KIO::StatJob::DestinationSide, 0); KJobWidgets::setWindow(statJob, m_view); if (statJob->exec()) { QString mes=i18n("The file %1 exists.\n" "Do you want to overwrite it?", url.url()); result = KMessageBox::warningContinueCancel (m_view, mes, QString(), KStandardGuiItem::overwrite()); if (result == KMessageBox::Cancel) return; } } while (result == KMessageBox::No); m_gameURL = url; } QTemporaryFile tempFile; tempFile.open(); KConfig config (tempFile.fileName(), KConfig::SimpleConfig); KConfigGroup main (&config, "KJumpingCube"); main.writeEntry ("Version", KJC_VERSION); KConfigGroup game (&config, "Game"); saveProperties (game); config.sync(); KIO::FileCopyJob *job = KIO::file_copy(QUrl::fromLocalFile(tempFile.fileName()), m_gameURL, -1, KIO::Overwrite); KJobWidgets::setWindow(job, m_view); job->exec(); if (! job->error() ) { emit statusMessage (i18n("Game saved as %1", m_gameURL.url()), false); } else { KMessageBox::sorry (m_view, i18n("There was an error in saving file\n%1", m_gameURL.url())); } } void Game::loadGame() { bool fileOk=true; QUrl url; do { - url = QFileDialog::getOpenFileUrl (m_view, QString(), m_gameURL, "*.kjc"); + url = QFileDialog::getOpenFileUrl (m_view, QString(), m_gameURL, QStringLiteral("*.kjc")); if (url.isEmpty()) return; KIO::StatJob* statJob = KIO::stat(url, KIO::StatJob::SourceSide, 0); KJobWidgets::setWindow(statJob, m_view); if (! statJob->exec()) { QString mes = i18n("The file %1 does not exist!", url.url()); KMessageBox::sorry (m_view, mes); fileOk = false; } } while (! fileOk); QTemporaryFile tempFile; tempFile.open(); KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tempFile.fileName()), -1, KIO::Overwrite); KJobWidgets::setWindow(job, m_view); job->exec(); if (! job->error() ) { KConfig config( tempFile.fileName(), KConfig::SimpleConfig); KConfigGroup main (&config, "KJumpingCube"); if (! main.hasKey ("Version")) { QString mes = i18n("The file %1 is not a KJumpingCube gamefile!", url.url()); KMessageBox::sorry (m_view, mes); return; } m_gameURL = url; KConfigGroup game (&config, "Game"); readProperties (game); emit setAction (UNDO, false); } else KMessageBox::sorry (m_view, i18n("There was an error loading file\n%1", url.url())); } void Game::undo() { bool moreToUndo = undoRedo (-1); emit setAction (UNDO, moreToUndo); emit setAction (REDO, true); } void Game::redo() { bool moreToRedo = undoRedo (+1); emit setAction (REDO, moreToRedo); emit setAction (UNDO, true); } bool Game::newGameOK() { if ((m_moveNo == 0) || (m_moveNo >= m_endMoveNo)) { // OK: game finished or not yet started. Settings might have changed. return true; } // Check if it is OK to abandon the current game. QString query; if (m_side != Prefs::cubeDim()) { query = i18n("You have changed the size setting of the game and " "that requires a new game to start.\n\n" "Do you wish to abandon the current game or continue " "playing and restore the previous size setting?"); } else { query = i18n("You have requested a new game, but " "there is already a game in progress.\n\n" "Do you wish to abandon the current game?"); } qCDebug(KJUMPINGCUBE_LOG) << "QUERY:" << query; int reply = KMessageBox::questionYesNo (m_view, query, i18n("New Game?"), KGuiItem (i18n("Abandon Game")), KGuiItem (i18n("Continue Game"))); if (reply == KMessageBox::Yes) { qCDebug(KJUMPINGCUBE_LOG) << "ABANDON GAME"; return true; // Start a new game. } if (m_side != Prefs::cubeDim()) { // Restore the setting: also the dialog-box copy if it has been created. qCDebug(KJUMPINGCUBE_LOG) << "Reset size" << Prefs::cubeDim() << "back to" << m_side; Prefs::setCubeDim (m_side); if (m_settingsPage) { m_settingsPage->kcfg_CubeDim->setValue (m_side); } Prefs::self()->save(); } qCDebug(KJUMPINGCUBE_LOG) << "CONTINUE GAME"; return false; // Continue the current game. } void Game::reset() { m_view->reset(); m_box->clear(); m_fullSpeed = Prefs::animationNone(); // Animate cascade moves? m_currentPlayer = One; m_waitingState = computerPlOne ? ComputerToMove : Nil; qCDebug(KJUMPINGCUBE_LOG) << "RESET: activity" << m_activity << "wait" << m_waitingState; #if AILog > 0 m_ai->startStats(); #endif } bool Game::undoRedo (int change) { Player oldPlayer = m_currentPlayer; bool isAI = false; int index = 0; bool moreToDo = (change < 0) ? m_box->undoPosition (m_currentPlayer, isAI, index) : m_box->redoPosition (m_currentPlayer, isAI, index); // Update the cube display after an undo or redo. for (int n = 0; n < (m_side * m_side); n++) { m_view->displayCube (n, m_box->owner (n), m_box->value (n)); m_view->highlightCube (n, false); } m_view->timedCubeHighlight (index); // Show which cube was moved. m_moveNo = m_moveNo + change; if (m_moveNo < m_endMoveNo) { if (oldPlayer != m_currentPlayer) { emit playerChanged (m_currentPlayer); } m_waitingState = isComputer (m_currentPlayer) ? ComputerToMove : m_waitingState; setUpNextTurn(); } else { // End of game: show winner. moreToDo = false; m_currentPlayer = oldPlayer; showWinner(); } return moreToDo; } void Game::setDim (int d) { if (d != m_side) { shutdown(); delete m_box; m_box = new AI_Box (this, d); qCDebug(KJUMPINGCUBE_LOG) << "AI_Box CONSTRUCTED by Game::setDim()"; m_side = d; m_view->setDim (d); } } void Game::shutdown() { // Shut down gracefully, avoiding a possible crash when the user hits Quit. m_view->killAnimation(); // Stop animation immediately (if active). if (m_activity == Computing) { m_ai->stop(); // Stop AI ASAP (moveCalculationDone() => Idle). m_activity = Aborting; } else if (m_activity == Stopping) { m_activity = Aborting; } else if (m_activity != Aborting) { m_activity = Idle; // In case it was ShowingMove or AnimatingMove. } } void Game::saveProperties (KConfigGroup & config) { // Save the current player. config.writeEntry ("onTurn", (int) m_currentPlayer); QStringList list; QString owner, value, key; // Save the position currently on the board. for (int x = 0; x < m_side; x++) { for (int y = 0; y < m_side; y++) { key.sprintf ("%u,%u", x, y); int index = x * m_side + y; owner.sprintf ("%u", m_box->owner (index)); value.sprintf ("%u", m_box->value (index)); list.append (owner.toLatin1()); list.append (value.toLatin1()); config.writeEntry (key, list); list.clear(); } } // Save the game and player settings. config.writeEntry ("CubeDim", m_side); config.writeEntry ("PauseForComputer", m_pauseForComputer ? 1 : 0); config.writeEntry ("ComputerPlayer1", computerPlOne); config.writeEntry ("ComputerPlayer2", computerPlTwo); config.writeEntry ("Kepler1", Prefs::kepler1()); config.writeEntry ("Kepler2", Prefs::kepler2()); config.writeEntry ("Newton1", Prefs::newton1()); config.writeEntry ("Newton2", Prefs::newton2()); config.writeEntry ("Skill1", Prefs::skill1()); config.writeEntry ("Skill2", Prefs::skill2()); } void Game::readProperties (const KConfigGroup& config) { QStringList list; QString key; int owner, value, maxValue; // Dimension must be 3 to 15 (see definition in ai_box.h). int cubeDim = config.readEntry ("CubeDim", minSide); if ((cubeDim < minSide) || (cubeDim > maxSide)) { KMessageBox::sorry (m_view, i18n("The file's cube box size is outside " "the range %1 to %2. It will be set to %1.", minSide, maxSide)); cubeDim = 3; } m_side = 1; // Create a new cube box. setDim (cubeDim); reset(); // IDW TODO - NEEDED? Is newGame() init VALID here? for (int x = 0; x < m_side; x++) { for (int y = 0; y < m_side; y++) { key.sprintf ("%u,%u", x, y); list = config.readEntry (key, QStringList()); // List length must be 2, owner must be 0-2, value >= 1 and <= max(). if (list.count() < 2) { KMessageBox::sorry (m_view, i18n("Missing input line for cube %1.", key)); owner = 0; value = 1; } else { owner = list.at(0).toInt(); value = list.at(1).toInt(); } if ((owner < 0) || (owner > 2)) { KMessageBox::sorry (m_view, i18n("Owner of cube %1 is outside the " "range 0 to 2.", key)); owner = 0; } int index = x * m_side + y; maxValue = (owner == 0) ? 1 : m_box->maxValue (index); if ((value < 1) || (value > maxValue)) { KMessageBox::sorry (m_view, i18n("Value of cube %1 is outside the " "range 1 to %2.", key, maxValue)); value = maxValue; } m_view->displayCube (index, (Player) owner, value); m_box->setOwner (index, (Player) owner); m_box->setValue (index, value); list.clear(); } } // Set current player - must be 1 or 2. int onTurn = config.readEntry ("onTurn", 1); if ((onTurn < 1) || (onTurn > 2)) { KMessageBox::sorry (m_view, i18n("Current player is neither 1 nor 2.")); onTurn = 1; } m_currentPlayer = (Player) onTurn; emit playerChanged (m_currentPlayer); // Restore the game and player settings. loadSavedSettings (config); Prefs::self()->save(); setUpNextTurn(); } void Game::loadSavedSettings (const KConfigGroup& config) { showSettingsDialog (false); // Load the settings dialog but do not show it. if (m_side != Prefs::cubeDim()) { // Update the size setting for the loaded game. Prefs::setCubeDim (m_side); m_settingsPage->kcfg_CubeDim->setValue (m_side); } int pause = config.readEntry ("PauseForComputer", -1); if (pause < 0) { // Older files will not contain more settings, return; // so keep the existing settings. } // Load the PauseForComputer and player settings. bool boolValue = pause > 0 ? true : false; Prefs::setPauseForComputer (boolValue); m_settingsPage->kcfg_PauseForComputer->setChecked (boolValue); m_pauseForComputer = boolValue; boolValue = config.readEntry ("ComputerPlayer1", false); Prefs::setComputerPlayer1 (boolValue); m_settingsPage->kcfg_ComputerPlayer1->setChecked (boolValue); computerPlOne = boolValue; boolValue = config.readEntry ("ComputerPlayer2", true); Prefs::setComputerPlayer2 (boolValue); m_settingsPage->kcfg_ComputerPlayer2->setChecked (boolValue); computerPlTwo = boolValue; boolValue = config.readEntry ("Kepler1", true); Prefs::setKepler1 (boolValue); m_settingsPage->kcfg_Kepler1->setChecked (boolValue); boolValue = config.readEntry ("Kepler2", true); Prefs::setKepler2 (boolValue); m_settingsPage->kcfg_Kepler2->setChecked (boolValue); boolValue = config.readEntry ("Newton1", false); Prefs::setNewton1 (boolValue); m_settingsPage->kcfg_Newton1->setChecked (boolValue); boolValue = config.readEntry ("Newton2", false); Prefs::setNewton2 (boolValue); m_settingsPage->kcfg_Newton2->setChecked (boolValue); int intValue = config.readEntry ("Skill1", 2); Prefs::setSkill1 (intValue); m_settingsPage->kcfg_Skill1->setValue (intValue); intValue = config.readEntry ("Skill2", 2); Prefs::setSkill2 (intValue); m_settingsPage->kcfg_Skill2->setValue (intValue); m_ai->setSkill (Prefs::skill1(), Prefs::kepler1(), Prefs::newton1(), Prefs::skill2(), Prefs::kepler2(), Prefs::newton2()); } bool Game::isComputer (Player player) const { if (player == One) return computerPlOne; else return computerPlTwo; } void Game::animationDone (int index) { if (m_activity == ShowingMove) { showingDone (index); } else { stepAnimationDone (index); } } diff --git a/game.h b/game.h index bcc0df6..c8c6905 100644 --- a/game.h +++ b/game.h @@ -1,314 +1,314 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer Copyright (C) 2012-2013 by Ian Wadham 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. **************************************************************************** */ #ifndef GAME_H #define GAME_H #include "ai_globals.h" #include #include // IDW test. #include #include class KConfigGroup; class KCubeBoxWidget; class SettingsWidget; class AI_Main; class AI_Box; /** * Codes for actions available to users of the Game class. */ enum Action {NEW, HINT, BUTTON, UNDO, REDO, SAVE, SAVE_AS, LOAD}; class Game : public QObject { Q_OBJECT public: explicit Game (const int dim, KCubeBoxWidget * view, QWidget * parent = 0); virtual ~Game(); /** * Make sure all animation and AI activity is over before closing a game. * */ void shutdown(); public slots: /** * Perform one of the game-actions listed in enum type Action. * * @param action The action to be performed, taken from enum Action. * This parameter must be of type int because KJumpingCube * invokes gameActions() via a QSignalMapper of int type. */ void gameActions (const int action); private: /** * Show the winner when the game ends, either after a move or a redo. */ void showWinner(); private slots: /** * Pop up the settings/preferences/configuration dialog window. */ void showSettingsDialog (bool show = true); /** * Check changes in the user's preferences or settings. Some settings can * be used immediately, some must wait until the start of the next turn and * a change in box size may require the user's OK (or not) to abandon a game * in progress and start a new game. */ void newSettings(); /** * Check if cube at [x, y] is available to the current (human) player. * If it is, make a move using doMove() and the cube at [x, y], animating * the move, if required. */ void startHumanMove (int x, int y); private: /** * Check if the current player is a computer player and, if so, start the * next move or perhaps wait for the user to click the button to show that * he or she is ready. Otherwise, wait for a human player to move. */ void setUpNextTurn(); /** * Starts calculating a computer move or hint (using asynchronous AI thread). */ void computeMove(); private slots: /** * Deliver a computer-move or hint calculated by the AI thread and start an * animation to show which cube is to move. * * @param index The position of the move in the array of cubes. If the box * has n x n cubes, index = x * n + y for co-ordinates [x, y]. */ void moveCalculationDone (int index); private: /** * Finish showing a computer move or hint. If it is a computer move, use * doMove() to make the actual move and animate it, if required. */ void showingDone (int index); /** * Increase the cube at 'index' and start the animation loop, if required. */ void doMove (int index); /** * Perform and display one or more steps of a move, with breaks for animation * if required, finishing when all steps have been done or the current player * has won the game. If there is no animation or if the user clicks the * action button to interrupt the animation, perform all steps at full speed. */ void doStep(); /** * Finish one step of a move, if animating, and use doStep() to continue. */ void stepAnimationDone (int index); /** * Finish an entire move. */ void moveDone(); /** * Switch to the other player, human or computer, after a non-winning move. */ Player changePlayer(); /** * Initiate or interrupt computer-move or animation activities. This is used * when the computer is first to move or when the user wishes to interrupt * or step through moves, as in a computer v. computer game or a complex * series of cascade moves. */ void buttonClick(); /** * Set the action-button to be red, enabled and with text relevant to * stopping the current activity (AI v AI game OR computing, showing or * animating a move). */ void setStopAction(); /** * Act on changes in settings that can be used immediately. Color changes * can take effect when control returns to the event loop, the other settings * next time they are used. They include animation settings, AI players and * their skill levels. */ void loadImmediateSettings(); /** * Act on changes in the user's choice of players and computer-player pause. * These can take effect only at the start of a turn. */ void loadPlayerSettings(); private slots: /** * Indicate that showing a move or animating a move-step has finished. * * @param index The position of the move in the array of cubes. If the box * has n x n cubes, index = x * n + y for co-ordinates [x, y]. */ void animationDone (int index); signals: /** * Request that the current player be shown in the KJumpingCube main window. * * @param player The ID of the player (= 1 or 2). */ void playerChanged (int newPlayer); /** * Request a change of appearance or text of the general-purpose action * button in the KJumpingCube main window. * * @param enabled True = button is active, false = button is inactive. * @param stop If active, true = use STOP style, false = use GO style. * @param caption Translated text to appear on the button. */ void buttonChange (bool enabled, bool stop = false, - const QString & caption = QString("")); + const QString & caption = QLatin1String("")); /** * Request enabling or disabling of an action by KJumpingCube main window. * * @param a The game-action to be enabled or disabled. * @param onOff True = enable the action, false = disable it. */ void setAction (const Action a, const bool onOff); /** * Request display of a status message. * * @param message The message to be displayed (translated). * @param timed If true, display the message for a limited time. */ void statusMessage (const QString & message, bool timed); protected: void saveProperties(KConfigGroup&); void readProperties(const KConfigGroup&); private: void loadSavedSettings (const KConfigGroup& config); private: QTime t; // IDW test. enum Activity {Idle, Computing, Stopping, Aborting, ShowingMove, AnimatingMove}; enum WaitingState {Nil, WaitingToStep, ComputerToMove}; Activity m_activity; // Current computer activity. WaitingState m_waitingState; // Current pause state. bool m_waitingToMove; // If true, AI paused or human to move. int m_moveNo; // Current move number, 0 = not started. int m_endMoveNo; // Winning move number, if game is over. bool m_interrupting; // If user has interrupted AI v. AI. bool m_newSettings; // If new player settings during a move. QWidget * m_parent; // Game object's parent (main window). KCubeBoxWidget * m_view; // Displayed cubes. // A pointer to the Settings widget defined in Qt Designer, when the // Preferences dialog is first created by Game::showSettingsDialog(). SettingsWidget * m_settingsPage; // Displayed settings, 0 = not yet used. AI_Box * m_box; // Game engine's cubes. int m_side; // Cube box size, from Prefs::cubeDim(). Player m_currentPlayer; // Current player: One or Two. QList * m_steps; // Steps in a move to be displayed. AI_Main * m_ai; // Current computer player. int m_index; // Location of move. bool m_fullSpeed; // If true, no animation of moves. bool computerPlOne; // If true, Player 1 is an AI. bool computerPlTwo; // If true, Player 2 is an AI. bool m_pauseForComputer; // If true, pause before each AI move. bool m_pauseForStep; // If true, pause before animation step. QUrl m_gameURL; // Location of load/save file. private slots: void newGame(); // Slot needed for queued invocation. private: void saveGame (bool saveAs); // Standard save-game action. void loadGame(); // Standard load-game action. void undo(); // Standard undo-move action. void redo(); // Standard redo-move action. /** * Check if it is OK to start a new game, maybe ending a current game. */ bool newGameOK(); /** * Reset cubebox for a new game. */ void reset(); /** * Undo or redo a move. * * @param change -1 = undo, +1 = redo * * @return true = More moves to undo/redo, false = No more to undo/redo. */ bool undoRedo (int change); /** * Set the number of cubes in a row or column. If the number has changed, * delete the existing set of cubes and create a new one. */ virtual void setDim (int dim); /** * Returns true if the player is a computer player. */ bool isComputer (Player player) const; inline void restoreGame(const KConfigGroup&c) { readProperties(c); } }; #endif // GAME_H diff --git a/kcubeboxwidget.cpp b/kcubeboxwidget.cpp index a55c256..058a807 100644 --- a/kcubeboxwidget.cpp +++ b/kcubeboxwidget.cpp @@ -1,515 +1,515 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer Copyright (C) 2012-2013 by Ian Wadhan 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 "kcubeboxwidget.h" #include #include #include #include #include #include #include #include "prefs.h" #include "kjumpingcube_debug.h" KCubeBoxWidget::KCubeBoxWidget (const int d, QWidget *parent) : QWidget (parent), m_side (d), m_popup (new QLabel (this)) { qCDebug(KJUMPINGCUBE_LOG) << "CONSTRUCT KCubeBoxWidget: side" << m_side; cubes.clear(); init(); } KCubeBoxWidget::~KCubeBoxWidget() { } bool KCubeBoxWidget::loadSettings() { qCDebug(KJUMPINGCUBE_LOG) << "LOAD VIEW SETTINGS"; bool reColorCubes = ((color1 != Prefs::color1()) || (color2 != Prefs::color2()) || (color0 != Prefs::color0())); color1 = Prefs::color1(); color2 = Prefs::color2(); color0 = Prefs::color0(); if (Prefs::animationNone()) { cascadeAnimation = None; } else if (Prefs::animationDelay() || (Prefs::animationSpeed() <= 1)) { cascadeAnimation = Darken; } else if (Prefs::animationBlink()) { cascadeAnimation = RapidBlink; } else if (Prefs::animationSpread()) { cascadeAnimation = Scatter; } animationTime = Prefs::animationSpeed() * 150; // NOTE: When the box-size (Prefs::cubeDim()) changes, Game::newGame() calls // KCubeBoxWidget::loadSettings() first, then KCubeBoxWidget::setDim(). if (reColorCubes) { makeStatusPixmaps (sWidth); // Make new status pixmaps. makeSVGCubes (cubeSize); setColors (); } return reColorCubes; } void KCubeBoxWidget::reset() // Called if a player wins or requests New game. { for (KCubeWidget * cube : qAsConst(cubes)) { cube->reset(); } KCubeWidget::enableClicks(true); currentAnimation = None; } void KCubeBoxWidget::displayCube (int index, Player owner, int value) { cubes.at(index)->setOwner (owner); cubes.at(index)->setValue (value); } void KCubeBoxWidget::highlightCube (int index, bool highlight) { if (highlight) { cubes.at(index)->setDark(); } else { cubes.at(index)->setNeutral(); } } void KCubeBoxWidget::timedCubeHighlight (int index) { if (m_highlighted > 0) { highlightDone(); } cubes.at(index)->setDark(); m_highlighted = index; m_highlightTimer->start(); } void KCubeBoxWidget::highlightDone() { cubes.at(m_highlighted)->setNeutral(); m_highlightTimer->stop(); m_highlighted = -1; } void KCubeBoxWidget::setColors () { for (KCubeWidget * cube : qAsConst(cubes)) { cube->updateColors(); } } void KCubeBoxWidget::setDim(int d) { if (d != m_side) { m_side = d; initCubes(); reCalculateGraphics (width(), height()); reset(); } } /* ***************************************************************** ** ** slots ** ** ***************************************************************** */ void KCubeBoxWidget::setWaitCursor() { setCursor (Qt::BusyCursor); } void KCubeBoxWidget::setNormalCursor() { setCursor (Qt::PointingHandCursor); } bool KCubeBoxWidget::checkClick (int x, int y) { /* IDW TODO - Remove this from the view OR rewrite it as a MouseEvent(). * // IDW TODO - Write a new mouse-click event for KCubeBoxWidget? Remove the // one that KCubeWidget has? */ qCDebug(KJUMPINGCUBE_LOG) << "Emit mouseClick (" << x << y << ")"; emit mouseClick (x, y); return false; } /* ***************************************************************** ** ** initializing functions ** ** ***************************************************************** */ void KCubeBoxWidget::init() { currentAnimation = None; animationSteps = 12; animationCount = 0; setMinimumSize (200, 200); color1 = Prefs::color1(); // Set preferred colors. color2 = Prefs::color2(); color0 = Prefs::color0(); KgTheme theme((QByteArray())); - theme.readFromDesktopFile(QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/default.desktop")); + theme.readFromDesktopFile(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("pics/default.desktop"))); svg.load (theme.graphicsPath()); initCubes(); animationTime = Prefs::animationSpeed() * 150; animationTimer = new QTimer(this); m_highlightTimer = new QTimer(this); m_highlightTimer->setInterval (1500); m_highlighted = -1; connect(animationTimer, &QTimer::timeout, this, &KCubeBoxWidget::nextAnimationStep); connect(m_highlightTimer, &QTimer::timeout, this, &KCubeBoxWidget::highlightDone); setNormalCursor(); setPopup(); } void KCubeBoxWidget::initCubes() { qDeleteAll (cubes); cubes.clear(); int nCubes = m_side * m_side; for (int n = 0; n < nCubes; n++) { KCubeWidget * cube = new KCubeWidget (this); cubes.append (cube); cube->setCoordinates (n / m_side, n % m_side, m_side - 1); cube->setPixmaps (&elements); connect(cube, &KCubeWidget::clicked, this, &KCubeBoxWidget::checkClick); cube->show(); } } void KCubeBoxWidget::makeStatusPixmaps (const int width) { qreal d, p; QImage status (width, width, QImage::Format_ARGB32_Premultiplied); QPainter s (&status); sWidth = width; d = width/4.0; p = width/2.0; status.fill (0); - svg.render (&s, "player_1"); + svg.render (&s, QStringLiteral("player_1")); colorImage (status, color1, width); - svg.render (&s, "lighting"); - svg.render (&s, "pip", QRectF (p - d/2.0, p - d/2.0, d, d)); + svg.render (&s, QStringLiteral("lighting")); + svg.render (&s, QStringLiteral("pip"), QRectF (p - d/2.0, p - d/2.0, d, d)); status1 = QPixmap::fromImage (status); d = width/5.0; p = width/3.0; status.fill (0); - svg.render (&s, "player_2"); + svg.render (&s, QStringLiteral("player_2")); colorImage (status, color2, width); - svg.render (&s, "lighting"); - svg.render (&s, "pip", QRectF (p - d/2.0, p - d/2.0, d, d)); - svg.render (&s, "pip", QRectF (p + p - d/2.0, p + p - d/2.0, d, d)); + svg.render (&s, QStringLiteral("lighting")); + svg.render (&s, QStringLiteral("pip"), QRectF (p - d/2.0, p - d/2.0, d, d)); + svg.render (&s, QStringLiteral("pip"), QRectF (p + p - d/2.0, p + p - d/2.0, d, d)); s.end(); status2 = QPixmap::fromImage (status); } void KCubeBoxWidget::makeSVGBackground (const int w, const int h) { QImage img (w, h, QImage::Format_ARGB32_Premultiplied); QPainter p (&img); img.fill (0); - svg.render (&p, "background"); + svg.render (&p, QStringLiteral("background")); p.end(); background = QPixmap::fromImage (img); } void KCubeBoxWidget::makeSVGCubes (const int width) { QImage img (width, width, QImage::Format_ARGB32_Premultiplied); QPainter q; // Paints whole faces of the dice. QImage pip (width/7, width/7, QImage::Format_ARGB32_Premultiplied); QPainter r; // Paints the pips on the faces of the dice. QRectF rect (0, 0, width, width); qreal pc = 20.0; // % radius on corners. elements.clear(); for (int i = FirstElement; i <= LastElement; i++) { q.begin(&img); q.setPen (Qt::NoPen); if (i == Pip) { pip.fill (0); } else { img.fill (0); } // NOTE: "neutral", "player_1" and "player_2" from file "default.svg" cause // odd effects at the corners. You get a cleaner look if they are omitted. switch (i) { case Neutral: // svg.render (&q, "neutral"); q.setBrush (color0); q.drawRoundedRect (rect, pc, pc, Qt::RelativeSize); - svg.render (&q, "lighting"); + svg.render (&q, QStringLiteral("lighting")); break; case Player1: // svg.render (&q, "player_1"); q.setBrush (color1); q.drawRoundedRect (rect, pc, pc, Qt::RelativeSize); - svg.render (&q, "lighting"); + svg.render (&q, QStringLiteral("lighting")); break; case Player2: // svg.render (&q, "player_2"); q.setBrush (color2); q.drawRoundedRect (rect, pc, pc, Qt::RelativeSize); - svg.render (&q, "lighting"); + svg.render (&q, QStringLiteral("lighting")); break; case Pip: r.begin(&pip); - svg.render (&r, "pip"); + svg.render (&r, QStringLiteral("pip")); r.end(); break; case BlinkLight: - svg.render (&q, "blink_light"); + svg.render (&q, QStringLiteral("blink_light")); break; case BlinkDark: - svg.render (&q, "blink_dark"); + svg.render (&q, QStringLiteral("blink_dark")); break; default: break; } q.end(); elements.append ((i == Pip) ? QPixmap::fromImage (pip) : QPixmap::fromImage (img)); } } void KCubeBoxWidget::colorImage (QImage & img, const QColor & c, const int w) { QRgb rgba = c.rgba(); for (int i = 0; i < w; i++) { for (int j = 0; j < w; j++) { if (img.pixel (i, j) != 0) { img.setPixel (i, j, rgba); } } } } void KCubeBoxWidget::paintEvent (QPaintEvent * /* event unused */) { QPainter p (this); p.drawPixmap (0, 0, background); } void KCubeBoxWidget::resizeEvent (QResizeEvent * event) { reCalculateGraphics (event->size().width(), event->size().height()); } void KCubeBoxWidget::reCalculateGraphics (const int w, const int h) { int boxSize = qMin(w, h); int frameWidth = boxSize / 30; // qCDebug(KJUMPINGCUBE_LOG) << "boxSize" << boxSize << "frameWidth" << frameWidth; boxSize = boxSize - (2 * frameWidth); cubeSize = (boxSize / m_side); boxSize = (cubeSize * m_side); topLeft.setX ((w - boxSize)/2); topLeft.setY ((h - boxSize)/2); // qCDebug(KJUMPINGCUBE_LOG) << "Dimension:" << m_side << "cubeSize:" << cubeSize << "topLeft:" << topLeft; makeSVGBackground (w, h); makeSVGCubes (cubeSize); for (int x = 0; x < m_side; x++) { for (int y = 0; y < m_side; y++) { int index = x * m_side + y; cubes.at (index)->move ( topLeft.x() + (x * cubeSize), topLeft.y() + (y * cubeSize)); cubes.at (index)->resize (cubeSize, cubeSize); } } setPopup(); } QSize KCubeBoxWidget::sizeHint() const { return QSize(400,400); } /* ***************************************************************** ** ** other private functions ** ** ***************************************************************** */ void KCubeBoxWidget::startAnimation (bool cascading, int index) { int interval = 0; m_index = index; currentAnimation = cascading ? cascadeAnimation : ComputerMove; switch (currentAnimation) { case None: animationCount = 0; return; // Should never happen. break; case ComputerMove: interval = 150 + (Prefs::animationSpeed() - 1) * 50; // 150-600 msec. animationCount = 4; cubes.at (index)->setLight(); break; case Darken: interval = animationTime; animationCount = 1; cubes.at (index)->setDark(); break; case RapidBlink: interval = 60 + Prefs::animationSpeed() * 30; // 120-360 msec. animationCount = 4; cubes.at (index)->setLight(); break; case Scatter: interval = (animationTime + animationSteps/2) / animationSteps; animationCount = animationSteps; break; } animationTimer->setInterval (interval); animationTimer->start(); } void KCubeBoxWidget::nextAnimationStep() { animationCount--; if (animationCount < 1) { animationTimer->stop(); // Finish normally. cubes.at (m_index)->setNeutral(); currentAnimation = None; emit animationDone (m_index); return; } switch (currentAnimation) { case None: return; // Should not happen. break; case ComputerMove: case RapidBlink: if (animationCount%2 == 1) { // Set light or dark phase. cubes.at (m_index)->setDark(); } else { cubes.at (m_index)->setLight(); } break; case Darken: break; // Should never happen (1 tick). case Scatter: int step = animationSteps - animationCount; if (step <= 2) { // Set the animation phase. cubes.at (m_index)->shrink(1.0 - step * 0.3); } else if (step < 7) { cubes.at (m_index)->expand((step - 2) * 0.2); } else if (step == 7) { cubes.at (m_index)->expand(1.2); scatterDots (0); } else { scatterDots (step - 7); } break; } } void KCubeBoxWidget::scatterDots (int step) { Player player = cubes.at(m_index)->owner(); int d = m_side - 1; int x = m_index / m_side; int y = m_index % m_side; if (x > 0) cubes.at (m_index - m_side)->migrateDot (+1, 0, step, player); if (x < d) cubes.at (m_index + m_side)->migrateDot (-1, 0, step, player); if (y > 0) cubes.at (m_index - 1) ->migrateDot ( 0, +1, step, player); if (y < d) cubes.at (m_index + 1) ->migrateDot ( 0, -1, step, player); } int KCubeBoxWidget::killAnimation() { if (animationTimer->isActive()) { animationTimer->stop(); // Stop current animation immediately. } return m_index; } const QPixmap & KCubeBoxWidget::playerPixmap (const int p) { return ((p == 1) ? status1 : status2); } void KCubeBoxWidget::setPopup() { QFont f; f.setPixelSize ((int) (height() * 0.04 + 0.5)); f.setWeight (QFont::Bold); f.setStretch (QFont::Expanded); - m_popup->setStyleSheet("QLabel { color : rgba(255, 255, 255, 75%); }"); + m_popup->setStyleSheet(QStringLiteral("QLabel { color : rgba(255, 255, 255, 75%); }")); m_popup->setFont (f); m_popup->resize (width(), (int) (height() * 0.08 + 0.5)); m_popup->setAlignment (Qt::AlignCenter); } void KCubeBoxWidget::showPopup (const QString & message) { m_popup->setText (message); m_popup->move ((this->width() - m_popup->width()) / 2, (this->height() - m_popup->height()) / 2 + (cubes.at (0)->height() / 5)); m_popup->raise(); m_popup->show(); update(); } void KCubeBoxWidget::hidePopup() { m_popup->hide(); update(); } diff --git a/kjumpingcube.cpp b/kjumpingcube.cpp index 4e04042..c0efc3f 100644 --- a/kjumpingcube.cpp +++ b/kjumpingcube.cpp @@ -1,194 +1,194 @@ /***************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer 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 "kjumpingcube.h" #include "kcubeboxwidget.h" #include "settingswidget.h" #include "prefs.h" #include #include #include #include #include #include #include #include #include #include #include "kjumpingcube_debug.h" #define MESSAGE_TIME 2000 KJumpingCube::KJumpingCube() { // Make a KCubeBoxWidget with the user's currently preferred number of cubes. qCDebug(KJUMPINGCUBE_LOG) << "KJumpingCube::KJumpingCube() CONSTRUCTOR"; m_view = new KCubeBoxWidget (Prefs::cubeDim(), this); m_game = new Game (Prefs::cubeDim(), m_view, this); m_view->makeStatusPixmaps (30); - connect(m_game,SIGNAL(playerChanged(int)),SLOT(changePlayerColor(int))); - connect(m_game,SIGNAL(buttonChange(bool,bool,QString)), - SLOT(changeButton(bool,bool,QString))); - connect(m_game,SIGNAL(statusMessage(QString,bool)), - SLOT(statusMessage(QString,bool))); + connect(m_game,&Game::playerChanged,this, &KJumpingCube::changePlayerColor); + connect(m_game,&Game::buttonChange, + this, &KJumpingCube::changeButton); + connect(m_game,&Game::statusMessage, + this, &KJumpingCube::statusMessage); // Tell the KMainWindow that this is indeed the main widget. setCentralWidget (m_view); // init statusbar QString s = i18n("Current player:"); statusBar()->addPermanentWidget (new QLabel (s)); currentPlayer = new QLabel (); currentPlayer->setFrameStyle (QFrame::NoFrame); changePlayerColor(One); statusBar()->addPermanentWidget (currentPlayer); initKAction(); - connect (m_game, SIGNAL (setAction(Action,bool)), - SLOT (setAction(Action,bool))); + connect (m_game, &Game::setAction, + this, &KJumpingCube::setAction); m_game->gameActions (NEW); // Start a new game. } bool KJumpingCube::queryClose() { // Terminate the AI or animation cleanly if either one is active. // If the AI is active, quitting immediately could cause a crash. m_game->shutdown(); return true; } void KJumpingCube::initKAction() { QAction * action; QSignalMapper * gameMapper = new QSignalMapper (this); connect (gameMapper, SIGNAL (mapped(int)), m_game, SLOT (gameActions(int))); action = KStandardGameAction::gameNew (gameMapper, SLOT (map()), this); actionCollection()->addAction (action->objectName(), action); gameMapper->setMapping (action, NEW); action = KStandardGameAction::load (gameMapper, SLOT (map()), this); actionCollection()->addAction (action->objectName(), action); gameMapper->setMapping (action, LOAD); action = KStandardGameAction::save (gameMapper, SLOT (map()), this); actionCollection()->addAction (action->objectName(), action); gameMapper->setMapping (action, SAVE); action = KStandardGameAction::saveAs (gameMapper, SLOT (map()), this); actionCollection()->addAction (action->objectName(), action); gameMapper->setMapping (action, SAVE_AS); action = KStandardGameAction::hint (gameMapper, SLOT(map()), this); actionCollection()->addAction (action->objectName(), action); gameMapper->setMapping (action, HINT); action = KStandardGameAction::undo (gameMapper, SLOT (map()), this); actionCollection()->addAction (action->objectName(), action); gameMapper->setMapping (action, UNDO); action->setEnabled (false); action = KStandardGameAction::redo (gameMapper, SLOT (map()), this); actionCollection()->addAction (action->objectName(), action); gameMapper->setMapping (action, REDO); action->setEnabled (false); actionButton = new QPushButton (this); - actionButton->setObjectName ("ActionButton"); + actionButton->setObjectName (QStringLiteral("ActionButton")); // Action button's style sheet: parameters for red, green and clicked colors. buttonLook = "QPushButton#ActionButton { color: white; background-color: %1; " "border-style: outset; border-width: 2px; border-radius: 10px; " "border-color: beige; font: bold 14px; min-width: 10em; " "padding: 6px; margin: 5px; margin-left: 10px; } " "QPushButton#ActionButton:pressed { background-color: %2; " "border-style: inset; } " "QPushButton#ActionButton:disabled { color: white;" "border-color: beige; background-color: steelblue; }"; gameMapper->setMapping (actionButton, BUTTON); connect (actionButton, SIGNAL(clicked()), gameMapper, SLOT(map())); QWidgetAction *widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(actionButton); - actionCollection()->addAction (QLatin1String ("action_button"), widgetAction); + actionCollection()->addAction (QStringLiteral ("action_button"), widgetAction); changeButton (true, true); // Load the button's style sheet. changeButton (false); // Set the button to be inactive. action = KStandardAction::preferences (m_game, SLOT(showSettingsDialog()), actionCollection()); qCDebug(KJUMPINGCUBE_LOG) << "PREFERENCES ACTION is" << action->objectName(); action->setIconText (i18n("Settings")); action = KStandardGameAction::quit (this, SLOT (close()), this); actionCollection()->addAction (action->objectName(), action); setupGUI(); } void KJumpingCube::changeButton (bool enabled, bool stop, const QString & caption) { qCDebug(KJUMPINGCUBE_LOG) << "KJumpingCube::changeButton (" << enabled << stop << caption; if (enabled && stop) { // Red look (stop something). - actionButton->setStyleSheet (buttonLook.arg("rgb(210, 0, 0)") - .arg("rgb(180, 0, 0)")); + actionButton->setStyleSheet (buttonLook.arg(QStringLiteral("rgb(210, 0, 0)")) + .arg(QStringLiteral("rgb(180, 0, 0)"))); } else if (enabled) { // Green look (continue something). - actionButton->setStyleSheet (buttonLook.arg("rgb(0, 170, 0)") - .arg("rgb(0, 150, 0)")); + actionButton->setStyleSheet (buttonLook.arg(QStringLiteral("rgb(0, 170, 0)")) + .arg(QStringLiteral("rgb(0, 150, 0)"))); } actionButton->setText (caption); actionButton->setEnabled (enabled); } void KJumpingCube::changePlayerColor (int newPlayer) { currentPlayer->setPixmap (m_view->playerPixmap (newPlayer)); } void KJumpingCube::setAction (const Action a, const bool onOff) { // These must match enum Action (see file game.h) and be in the same order. const char * name [] = {"game_new", "move_hint", "action_button", "move_undo", "move_redo", "game_save", "game_saveAs", "game_load"}; ((QAction *) actionCollection()->action (name [a]))->setEnabled (onOff); } void KJumpingCube::statusMessage (const QString & message, bool timed) { if (timed) { statusBar()->showMessage (message, MESSAGE_TIME); } else { statusBar()->showMessage (message); } } diff --git a/main.cpp b/main.cpp index 7bace0c..de609da 100644 --- a/main.cpp +++ b/main.cpp @@ -1,82 +1,82 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer 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 "version.h" #include "kjumpingcube.h" #include #include #include #include #include #include #include static const char description[] = I18N_NOOP("Tactical one or two player game"); int main(int argc, char *argv[]) { QApplication app(argc, argv); Kdelibs4ConfigMigrator migrate(QStringLiteral("kjumpingcube")); migrate.setConfigFiles(QStringList() << QStringLiteral("kjumpingcuberc")); migrate.setUiFiles(QStringList() << QStringLiteral("kjumpingcubeui.rc")); migrate.migrate(); KLocalizedString::setApplicationDomain("kjumpingcube"); - KAboutData aboutData( "kjumpingcube", i18n("KJumpingCube"), + KAboutData aboutData( QStringLiteral("kjumpingcube"), i18n("KJumpingCube"), KJC_VERSION, i18n(description), KAboutLicense::GPL, i18n("(c) 1998-2000, Matthias Kiefer")); aboutData.setOrganizationDomain(QByteArray("kde.org")); - aboutData.addAuthor(i18n("Matthias Kiefer"),QString(), "matthias.kiefer@gmx.de"); - aboutData.addAuthor(i18n("Benjamin Meyer"),i18n("Various improvements"), "ben+kjumpingcube@meyerhome.net"); + aboutData.addAuthor(i18n("Matthias Kiefer"),QString(), QStringLiteral("matthias.kiefer@gmx.de")); + aboutData.addAuthor(i18n("Benjamin Meyer"),i18n("Various improvements"), QStringLiteral("ben+kjumpingcube@meyerhome.net")); aboutData.addCredit(i18n("Ian Wadham"), i18n("Upgrade to KDE4 and SVG artwork support."), - "iandw.au@gmail.com"); + QStringLiteral("iandw.au@gmail.com")); aboutData.addCredit(i18n("Eugene Trounev"), i18n("Graphics for KDE 4.0 version."), - "irs_me@hotmail.com"); - aboutData.setHomepage("http://games.kde.org/kjumpingcube"); + QStringLiteral("irs_me@hotmail.com")); + aboutData.setHomepage(QStringLiteral("http://games.kde.org/kjumpingcube")); QCommandLineParser parser; KAboutData::setApplicationData(aboutData); KCrash::initialize(); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); KDBusService service; - app.setWindowIcon(QIcon::fromTheme(QLatin1String("kjumpingcube"))); + app.setWindowIcon(QIcon::fromTheme(QStringLiteral("kjumpingcube"))); // All session management is handled in the RESTORE macro if (app.isSessionRestored()) { RESTORE(KJumpingCube) } else { KJumpingCube *kjumpingcube = new KJumpingCube; kjumpingcube->show(); } return app.exec(); }