File indexing completed on 2023-09-24 08:15:56
0001 /* 0002 SPDX-FileCopyrightText: 2006 Dmitry Suzdalev <dimsuz@gmail.com> 0003 SPDX-FileCopyrightText: 2010 Brian Croom <brian.s.croom@gmail.com> 0004 SPDX-FileCopyrightText: 2013 Denis Kuplyakov <dener.kup@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "mainwindow.h" 0010 0011 #include <QDebug> 0012 #include <QIcon> 0013 #include <QStatusBar> 0014 #include <QScreen> 0015 // KF 0016 #include <KActionCollection> 0017 #include <KLocalizedString> 0018 #include <KMessageBox> 0019 // KDEGames 0020 #include <KStandardGameAction> 0021 #include <kdegames_version.h> 0022 0023 #include "commondefs.h" 0024 #include "kreversihumanplayer.h" 0025 #include "kreversicomputerplayer.h" 0026 #include "kexthighscore.h" 0027 0028 KReversiMainWindow::KReversiMainWindow(QWidget* parent, bool startDemo) 0029 : KXmlGuiWindow(parent), 0030 m_startDialog(nullptr), 0031 m_view(nullptr), 0032 m_game(nullptr), 0033 m_historyDock(nullptr), 0034 m_historyView(nullptr), 0035 m_firstShow(true), 0036 m_startInDemoMode(startDemo), 0037 m_undoAct(nullptr), 0038 m_hintAct(nullptr) 0039 { 0040 memset(m_player, 0, sizeof(m_player)); 0041 0042 m_provider = new KgThemeProvider(); 0043 #if KDEGAMES_VERSION >= QT_VERSION_CHECK(7, 4, 0) 0044 m_provider->discoverThemes(QStringLiteral("pics")); 0045 #else 0046 m_provider->discoverThemes("appdata", QStringLiteral("pics")); 0047 #endif 0048 0049 for (auto &label : m_statusBarLabel) { 0050 label = new QLabel(this); 0051 label->setAlignment(Qt::AlignCenter); 0052 statusBar()->addWidget(label, 1); 0053 } 0054 m_statusBarLabel[common]->setText(i18n("Press start game!")); 0055 0056 // initialize difficulty stuff 0057 Kg::difficulty()->addStandardLevelRange( 0058 KgDifficultyLevel::VeryEasy, KgDifficultyLevel::Impossible, 0059 KgDifficultyLevel::Easy //default 0060 ); 0061 0062 KgDifficultyGUI::init(this); 0063 connect(Kg::difficulty(), &KgDifficulty::currentLevelChanged, this, &KReversiMainWindow::levelChanged); 0064 Kg::difficulty()->setEditable(false); 0065 0066 // initialize history dock 0067 m_historyView = new QListWidget(this); 0068 m_historyView->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); 0069 m_historyDock = new QDockWidget(i18n("Move History")); 0070 m_historyDock->setWidget(m_historyView); 0071 m_historyDock->setObjectName(QStringLiteral("history_dock")); 0072 0073 m_historyDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); 0074 addDockWidget(Qt::RightDockWidgetArea, m_historyDock); 0075 0076 // create main game view 0077 m_view = new KReversiView(m_game, this, m_provider); 0078 setCentralWidget(m_view); 0079 0080 // initialise dialog handler 0081 m_startDialog = new StartGameDialog(this, m_provider); 0082 connect(m_startDialog, &StartGameDialog::startGame, this, &KReversiMainWindow::slotDialogReady); 0083 0084 // initialise actions 0085 setupActionsInit(); 0086 0087 // load saved settings 0088 loadSettings(); 0089 0090 setupGUI(screen()->availableGeometry().size() * 0.7); 0091 m_historyDock->hide(); 0092 } 0093 0094 KReversiMainWindow::~KReversiMainWindow() 0095 { 0096 clearPlayers(); 0097 delete m_provider; 0098 } 0099 0100 void KReversiMainWindow::setupActionsInit() 0101 { 0102 // Common actions 0103 KStandardGameAction::gameNew(this, &KReversiMainWindow::slotNewGame, actionCollection()); 0104 KStandardGameAction::highscores(this, &KReversiMainWindow::slotHighscores, actionCollection()); 0105 KStandardGameAction::quit(this, &QWidget::close, actionCollection()); 0106 0107 // Undo 0108 m_undoAct = KStandardGameAction::undo(this, &KReversiMainWindow::slotUndo, actionCollection()); 0109 m_undoAct->setEnabled(false); // nothing to undo at the start of the game 0110 0111 // Hint 0112 m_hintAct = KStandardGameAction::hint(m_view, &KReversiView::slotHint, actionCollection()); 0113 m_hintAct->setEnabled(false); 0114 0115 // Last move 0116 m_showLast = new KToggleAction(QIcon::fromTheme(QStringLiteral("lastmoves")), i18n("Show Last Move"), this); 0117 actionCollection()->addAction(QStringLiteral("show_last_move"), m_showLast); 0118 connect(m_showLast, &KToggleAction::triggered, m_view, &KReversiView::setShowLastMove); 0119 0120 // Legal moves 0121 m_showLegal = new KToggleAction(QIcon::fromTheme(QStringLiteral("legalmoves")), i18n("Show Legal Moves"), this); 0122 actionCollection()->addAction(QStringLiteral("show_legal_moves"), m_showLegal); 0123 connect(m_showLegal, &KToggleAction::triggered, m_view, &KReversiView::setShowLegalMoves); 0124 0125 // Animation speed 0126 m_animSpeedAct = new KSelectAction(i18n("Animation Speed"), this); 0127 actionCollection()->addAction(QStringLiteral("anim_speed"), m_animSpeedAct); 0128 0129 QStringList acts; 0130 acts << i18n("Slow") << i18n("Normal") << i18n("Fast"); 0131 m_animSpeedAct->setItems(acts); 0132 connect(m_animSpeedAct, &KSelectAction::indexTriggered, this, &KReversiMainWindow::slotAnimSpeedChanged); 0133 0134 // Chip's color 0135 m_coloredChipsAct = new KToggleAction(i18n("Use Colored Chips"), this); 0136 actionCollection()->addAction(QStringLiteral("use_colored_chips"), m_coloredChipsAct); 0137 connect(m_coloredChipsAct, &KToggleAction::triggered, this, &KReversiMainWindow::slotUseColoredChips); 0138 0139 // Move history 0140 // NOTE: read/write this from/to config file? Or not necessary? 0141 m_showMovesAct = m_historyDock->toggleViewAction(); 0142 m_showMovesAct->setIcon(QIcon::fromTheme(QStringLiteral("view-history"))); 0143 m_showMovesAct->setText(i18n("Show Move History")); 0144 actionCollection()->addAction(QStringLiteral("show_moves"), m_showMovesAct); 0145 connect(m_historyDock, &QDockWidget::visibilityChanged, this, &KReversiMainWindow::slotToggleBoardLabels); 0146 } 0147 0148 void KReversiMainWindow::loadSettings() 0149 { 0150 // Animation speed 0151 m_animSpeedAct->setCurrentItem(Preferences::animationSpeed()); 0152 m_view->setAnimationSpeed(Preferences::animationSpeed()); 0153 0154 // Chip's color 0155 m_coloredChipsAct->setChecked(Preferences::useColoredChips()); 0156 m_view->setChipsPrefix(Preferences::useColoredChips() ? 0157 Colored : BlackWhite); 0158 m_startDialog->setChipsPrefix(Preferences::useColoredChips() ? 0159 Colored : BlackWhite); 0160 } 0161 0162 void KReversiMainWindow::levelChanged() 0163 { 0164 // we are assuming that level can be changed here only when it is 0165 // USER-AI or AI-USER match 0166 0167 int skill = Utils::difficultyLevelToInt(); 0168 0169 if (m_nowPlayingInfo.type[White] == GameStartInformation::AI) 0170 ((KReversiComputerPlayer *)(m_player[White]))->setSkill(skill); 0171 else if (m_nowPlayingInfo.type[Black] == GameStartInformation::Human) 0172 ((KReversiComputerPlayer *)(m_player[Black]))->setSkill(skill); 0173 } 0174 0175 void KReversiMainWindow::slotAnimSpeedChanged(int speed) 0176 { 0177 m_view->setAnimationSpeed(speed); 0178 Preferences::setAnimationSpeed(speed); 0179 Preferences::self()->save(); 0180 } 0181 0182 void KReversiMainWindow::slotUseColoredChips(bool toggled) 0183 { 0184 ChipsPrefix chipsPrefix = m_coloredChipsAct->isChecked() ? 0185 Colored : 0186 BlackWhite; 0187 m_view->setChipsPrefix(chipsPrefix); 0188 m_startDialog->setChipsPrefix(chipsPrefix); 0189 Preferences::setUseColoredChips(toggled); 0190 Preferences::self()->save(); 0191 } 0192 0193 void KReversiMainWindow::slotToggleBoardLabels(bool toggled) 0194 { 0195 m_view->setShowBoardLabels(toggled); 0196 } 0197 0198 void KReversiMainWindow::slotNewGame() 0199 { 0200 m_startDialog->exec(); 0201 } 0202 0203 void KReversiMainWindow::slotGameOver() 0204 { 0205 m_hintAct->setEnabled(false); 0206 m_undoAct->setEnabled(m_game->canUndo()); 0207 0208 int blackScore = m_game->playerScore(Black); 0209 int whiteScore = m_game->playerScore(White); 0210 0211 bool storeScore = false; 0212 KExtHighscore::Score score; 0213 0214 QString res; 0215 if (m_nowPlayingInfo.type[Black] == GameStartInformation::Human 0216 && m_nowPlayingInfo.type[White] == GameStartInformation::AI) { // we are playing black 0217 storeScore = true; 0218 KExtHighscore::setGameType(((KReversiComputerPlayer *)m_player[White])->lowestSkill()); 0219 score.setScore(blackScore); 0220 if (blackScore == whiteScore) { 0221 res = i18n("Game is drawn!"); 0222 score.setType(KExtHighscore::Draw); 0223 } else if (blackScore > whiteScore) { 0224 res = i18n("You win!"); 0225 score.setType(KExtHighscore::Won); 0226 } else { 0227 res = i18n("You have lost!"); 0228 score.setType(KExtHighscore::Lost); 0229 } 0230 } else if (m_nowPlayingInfo.type[White] == GameStartInformation::Human 0231 && m_nowPlayingInfo.type[Black] == GameStartInformation::AI) { // we are playing white 0232 storeScore = true; 0233 KExtHighscore::setGameType(((KReversiComputerPlayer *)m_player[Black])->lowestSkill()); 0234 score.setScore(whiteScore); 0235 if (blackScore == whiteScore) { 0236 res = i18n("Game is drawn!"); 0237 score.setType(KExtHighscore::Draw); 0238 } else if (blackScore < whiteScore) { 0239 res = i18n("You win!"); 0240 score.setType(KExtHighscore::Won); 0241 } else { 0242 res = i18n("You have lost!"); 0243 score.setType(KExtHighscore::Lost); 0244 } 0245 0246 } else if (m_nowPlayingInfo.type[Black] == GameStartInformation::Human 0247 && m_nowPlayingInfo.type[White] == GameStartInformation::Human) { // friends match 0248 if (blackScore == whiteScore) { 0249 res = i18n("Game is drawn!"); 0250 } else if (blackScore > whiteScore) { 0251 res = i18n("%1 has won!", m_nowPlayingInfo.name[Black]); 0252 } else { 0253 res = i18n("%1 has won!", m_nowPlayingInfo.name[White]); 0254 } 0255 } else { // using Black White names in other cases 0256 if (blackScore == whiteScore) { 0257 res = i18n("Game is drawn!"); 0258 } else if (blackScore > whiteScore) { 0259 res = i18n("%1 has won!", Utils::colorToString(Black)); 0260 } else { 0261 res = i18n("%1 has won!", Utils::colorToString(White)); 0262 } 0263 } 0264 0265 if (m_nowPlayingInfo.type[Black] == GameStartInformation::AI 0266 && m_nowPlayingInfo.type[White] == GameStartInformation::AI) { 0267 res += i18n("\n%1: %2", Utils::colorToString(Black), blackScore); 0268 res += i18n("\n%1: %2", Utils::colorToString(White), whiteScore); 0269 } else { 0270 res += i18n("\n%1: %2", m_nowPlayingInfo.name[Black], blackScore); 0271 res += i18n("\n%1: %2", m_nowPlayingInfo.name[White], whiteScore); 0272 } 0273 0274 KMessageBox::information(this, res, i18n("Game over")); 0275 0276 if (storeScore) 0277 KExtHighscore::submitScore(score, this); 0278 } 0279 0280 void KReversiMainWindow::slotMoveFinished() 0281 { 0282 updateHistory(); 0283 updateStatusBar(); 0284 0285 m_hintAct->setEnabled(m_game->isHintAllowed()); 0286 m_undoAct->setEnabled(m_game->canUndo()); 0287 } 0288 0289 void KReversiMainWindow::updateHistory() 0290 { 0291 MoveList history = m_game->getHistory(); 0292 m_historyView->clear(); 0293 0294 for (int i = 0; i < history.size(); i++) { 0295 QString numStr = QString::number(i + 1) + QStringLiteral(". "); 0296 m_historyView->addItem(numStr + Utils::moveToString(history.at(i))); 0297 } 0298 0299 QListWidgetItem *last = m_historyView->item(m_historyView->count() - 1); 0300 m_historyView->setCurrentItem(last); 0301 m_historyView->scrollToItem(last); 0302 } 0303 0304 void KReversiMainWindow::slotUndo() 0305 { 0306 // scene will automatically notice that it needs to update 0307 m_game->undo(); 0308 0309 updateHistory(); 0310 updateStatusBar(); 0311 0312 m_undoAct->setEnabled(m_game->canUndo()); 0313 m_hintAct->setEnabled(m_game->isHintAllowed()); 0314 } 0315 0316 void KReversiMainWindow::slotHighscores() 0317 { 0318 KExtHighscore::show(this); 0319 } 0320 0321 void KReversiMainWindow::slotDialogReady() 0322 { 0323 GameStartInformation info = m_startDialog->createGameStartInformation(); 0324 receivedGameStartInformation(info); 0325 } 0326 0327 void KReversiMainWindow::showEvent(QShowEvent*) 0328 { 0329 if (m_firstShow && m_startInDemoMode) { 0330 qDebug() << "starting demo..."; 0331 startDemo(); 0332 } else if (m_firstShow) { 0333 QTimer::singleShot(0, this, &KReversiMainWindow::slotNewGame); 0334 } 0335 m_firstShow = false; 0336 } 0337 0338 void KReversiMainWindow::updateStatusBar() 0339 { 0340 if (m_game->isGameOver()) { 0341 m_statusBarLabel[common]->setText(i18n("GAME OVER")); 0342 } 0343 0344 if (m_nowPlayingInfo.type[Black] == GameStartInformation::AI 0345 && m_nowPlayingInfo.type[White] == GameStartInformation::AI) { // using Black White names 0346 m_statusBarLabel[black]->setText(i18n("%1: %2", Utils::colorToString(Black), m_game->playerScore(Black))); 0347 m_statusBarLabel[white]->setText(i18n("%1: %2", Utils::colorToString(White), m_game->playerScore(White))); 0348 0349 if (!m_game->isGameOver()) { 0350 m_statusBarLabel[common]->setText(i18n("%1 turn", Utils::colorToString(m_game->currentPlayer()))); 0351 } 0352 } else { // using player's names 0353 m_statusBarLabel[black]->setText(i18n("%1: %2", m_nowPlayingInfo.name[Black], m_game->playerScore(Black))); 0354 m_statusBarLabel[white]->setText(i18n("%1: %2", m_nowPlayingInfo.name[White], m_game->playerScore(White))); 0355 0356 if (!m_game->isGameOver() && m_game->currentPlayer() != NoColor) { 0357 m_statusBarLabel[common]->setText(i18n("%1's turn", m_nowPlayingInfo.name[m_game->currentPlayer()])); 0358 } 0359 } 0360 } 0361 0362 0363 // TODO: test it!!! 0364 void KReversiMainWindow::startDemo() 0365 { 0366 GameStartInformation info; 0367 info.name[0] = info.name[1] = i18n("Computer"); 0368 info.type[0] = info.type[1] = GameStartInformation::AI; 0369 info.skill[0] = info.skill[1] = Utils::difficultyLevelToInt(); 0370 0371 receivedGameStartInformation(info); 0372 } 0373 0374 void KReversiMainWindow::clearPlayers() 0375 { 0376 for (int i = 0; i < 2; i++) // iterating through white to black 0377 if (m_player[i]) { 0378 m_player[i]->disconnect(); 0379 delete m_player[i]; 0380 m_player[i] = nullptr; 0381 } 0382 } 0383 0384 void KReversiMainWindow::receivedGameStartInformation(const GameStartInformation &info) 0385 { 0386 clearPlayers(); 0387 m_nowPlayingInfo = info; 0388 0389 for (int i = 0; i < 2; i++) // iterating through black and white 0390 if (info.type[i] == GameStartInformation::AI) { 0391 m_player[i] = new KReversiComputerPlayer(ChipColor(i), info.name[i]); 0392 ((KReversiComputerPlayer *)(m_player[i]))->setSkill(info.skill[i]); 0393 levelChanged(); 0394 } else { 0395 m_player[i] = new KReversiHumanPlayer(ChipColor(i), info.name[i]); 0396 } 0397 0398 m_game = new KReversiGame(m_player[Black], m_player[White]); 0399 0400 m_view->setGame(m_game); 0401 0402 connect(m_game, &KReversiGame::gameOver, this, &KReversiMainWindow::slotGameOver); 0403 connect(m_game, &KReversiGame::moveFinished, this, &KReversiMainWindow::slotMoveFinished); 0404 0405 for (int i = 0; i < 2; i++) // iterating white to black 0406 if (info.type[i] == GameStartInformation::Human) 0407 connect(m_view, &KReversiView::userMove, 0408 (KReversiHumanPlayer *)(m_player[i]), &KReversiHumanPlayer::onUICellClick); 0409 0410 updateStatusBar(); 0411 updateHistory(); 0412 0413 if (info.type[White] == GameStartInformation::AI 0414 && info.type[Black] == GameStartInformation::Human) { 0415 Kg::difficulty()->setEditable(true); 0416 Kg::difficulty()->select(Utils::intToDifficultyLevel(info.skill[White])); 0417 } else if (info.type[White] == GameStartInformation::Human 0418 && info.type[Black] == GameStartInformation::AI) { 0419 Kg::difficulty()->setEditable(true); 0420 Kg::difficulty()->select(Utils::intToDifficultyLevel(info.skill[Black])); 0421 } else 0422 Kg::difficulty()->setEditable(false); 0423 0424 m_hintAct->setEnabled(m_game->isHintAllowed()); 0425 m_undoAct->setEnabled(m_game->canUndo()); 0426 } 0427 0428 #include "moc_mainwindow.cpp"