File indexing completed on 2022-09-27 13:12:50

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2007 by Albert Astals Cid <aacid@kde.org>          *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  ***************************************************************************/
0009 
0010 #include "kiriki.h"
0011 
0012 #include <stdlib.h>
0013 
0014 #include <QHBoxLayout>
0015 #include <QHeaderView>
0016 #include <QItemSelectionModel>
0017 #include <QPainter>
0018 #include <QPointer>
0019 #include <QPrintDialog>
0020 #include <QPrinter>
0021 #include <QStyledItemDelegate>
0022 #include <QTimer>
0023 #include <QTreeView>
0024 #include <QDateTime>
0025 
0026 #include <QApplication>
0027 #include <KConfigDialog>
0028 #include <KLocalizedString>
0029 #include <KMessageBox>
0030 #include <highscore/kscoredialog.h>
0031 #include <KStandardAction>
0032 #include <KStandardGameAction>
0033 #include <KActionCollection>
0034 #include <QAction>
0035 #include <QStatusBar>
0036 #include <KToggleAction>
0037 #include "computer.h"
0038 #include "configwidget.h"
0039 #include "lateralwidget.h"
0040 #include "scores.h"
0041 #include "settings.h"
0042 #include "itemdelegate.h"
0043 
0044 kiriki::kiriki() : KXmlGuiWindow(), m_hintGiven(false)
0045 {
0046     QWidget *w = new QWidget(this);
0047     QHBoxLayout *lay = new QHBoxLayout(w);
0048     
0049     m_lateral = new lateralWidget(w);
0050     lay -> addWidget(m_lateral);
0051     
0052     m_scoresWidget = new QTreeView(w);
0053 
0054     m_delegateHighlighted = new QStyledItemDelegate(m_scoresWidget);
0055 
0056     if (kirikiSettings::fontSize() > kirikiSettings::rowHeight())
0057     {
0058         kirikiSettings::setRowHeight(kirikiSettings::fontSize());
0059     }
0060 
0061     m_itemDelegate = new itemDelegate(kirikiSettings::rowHeight(), m_scoresWidget);
0062 
0063     m_scoresWidget -> setItemDelegate(m_itemDelegate);
0064     m_scoresWidget -> setSelectionBehavior(QAbstractItemView::SelectRows);
0065     m_scoresWidget -> setRootIsDecorated(false);
0066     m_scoresWidget -> header() -> setSectionResizeMode(QHeaderView::Stretch);
0067     m_scoresWidget -> header() -> setSectionsMovable(false);
0068     m_scoresWidget -> header() -> setStretchLastSection(false);
0069     m_scoresWidget -> header() -> hide();
0070     m_scoresWidget -> setItemsExpandable(false);
0071 
0072     // set minimum section size so that you don't get ... when making the widget small
0073     QFont f = m_scoresWidget -> font();
0074     f.setBold(true);
0075     f.setPointSize(f.pointSize() + 5);
0076     QFontMetrics fm(f);
0077         m_scoresWidget -> header() -> setMinimumSectionSize(fm.boundingRect(QStringLiteral("9999")).width());
0078 
0079     connect(m_scoresWidget, &QTreeView::pressed, this, &kiriki::pressed);
0080     lay -> addWidget(m_scoresWidget, 1);
0081     
0082     m_scores = nullptr;
0083     
0084     // Game
0085     QAction *gameNewAction = KStandardGameAction::gameNew(this, &kiriki::newGame, actionCollection());
0086     KStandardGameAction::highscores(this, &kiriki::showHighScores, actionCollection());
0087     KStandardGameAction::print(this, &kiriki::print, actionCollection());
0088     KStandardGameAction::quit(qApp, &QApplication::quit, actionCollection());
0089     m_hintAction = KStandardGameAction::hint(this, &kiriki::showHint, actionCollection());
0090     m_demoAction = KStandardGameAction::demo(this, &kiriki::demo, actionCollection());
0091     connect(gameNewAction, &QAction::triggered, m_demoAction, &KToggleAction::setChecked);
0092     connect(gameNewAction, &QAction::triggered, m_demoAction, &KToggleAction::setDisabled);
0093     connect(gameNewAction, &QAction::triggered, m_hintAction, &QAction::setDisabled);
0094     connect(gameNewAction, &QAction::triggered, m_lateral, &lateralWidget::disableDemoMode);
0095     connect(gameNewAction, &QAction::triggered, m_lateral, &lateralWidget::unhighlightAllDice);
0096     connect(this, &kiriki::demoStarted, m_demoAction, &KToggleAction::setDisabled);
0097     connect(this, &kiriki::demoStarted, m_demoAction, &KToggleAction::setChecked);
0098     connect(this, &kiriki::demoStarted, m_hintAction, &QAction::setDisabled);
0099     connect(this, &kiriki::demoStarted, m_lateral, &lateralWidget::unhighlightAllDice);
0100     connect(m_lateral, &lateralWidget::newGameClicked, gameNewAction, &QAction::trigger);
0101     
0102     // Preferences
0103     KStandardAction::preferences(this, &kiriki::showPreferences, actionCollection());
0104     
0105     setCentralWidget(w);
0106     setupGUI(Keys | Save | Create);
0107     show();
0108     
0109     if (kirikiSettings::startupDemoEnabled()) m_demoAction -> trigger();
0110     else newGame();
0111 }
0112 
0113 void kiriki::pressed(const QModelIndex &index)
0114 {
0115     if (!m_scores -> currentPlayer().isHuman()) return;
0116 
0117     if (index.column() == 0 || index.column() == m_scores -> currentPlayerNumber() + 1) play(index);
0118 
0119     m_scoresWidget -> setItemDelegateForRow(m_highlightedRowIndex, nullptr);
0120 }
0121 
0122 void kiriki::play(const QModelIndex &index)
0123 {
0124     play(m_scores->row(index.row()).scoreRow());
0125 }
0126 
0127 void kiriki::play(int scoreRow)
0128 {
0129     int score = -1;
0130     
0131     if (scoreRow == 0) score = m_lateral -> getOnes();
0132     else if (scoreRow == 1) score = m_lateral -> getTwos();
0133     else if (scoreRow == 2) score = m_lateral -> getThrees();
0134     else if (scoreRow == 3) score = m_lateral -> getFours();
0135     else if (scoreRow == 4) score = m_lateral -> getFives();
0136     else if (scoreRow == 5) score = m_lateral -> getSixs();
0137     else if (scoreRow == 6) score = m_lateral -> getThreeOfAKind();
0138     else if (scoreRow == 7) score = m_lateral -> getFourOfAKind();
0139     else if (scoreRow == 8) score = m_lateral -> getFullHouse();
0140     else if (scoreRow == 9) score = m_lateral -> getSStraight();
0141     else if (scoreRow == 10) score = m_lateral -> getLStraight();
0142     else if (scoreRow == 11) score = m_lateral -> getKiriki();
0143     else if (scoreRow == 12) score = m_lateral -> totalSum();
0144     else return;
0145     
0146     Q_ASSERT(score != -1);
0147     int row = m_scores -> rowForScoreRow(scoreRow);
0148     if (m_scores -> setData(m_scores -> index(row, 0), score, Qt::EditRole)) nextTurn();
0149 }
0150 
0151 void kiriki::newGame()
0152 {
0153     delete m_scores;
0154     m_scores = new scores();
0155     m_scores -> setParent(this);
0156     m_scoresWidget -> setModel(m_scores);
0157     m_lateral -> nextTurn();
0158 
0159     m_scoresWidget -> header() -> setSectionResizeMode(0, QHeaderView::Custom);
0160     m_scoresWidget -> resizeColumnToContents(0);
0161     statusBar()->hide();
0162     if (m_demoAction -> isChecked()) playComputer();
0163     connect(m_lateral, &lateralWidget::rolled, statusBar(), &QWidget::hide);
0164 }
0165 
0166 void kiriki::demo()
0167 {
0168     if(
0169         m_scores &&
0170         !m_scores -> allScores() &&
0171         !m_scores -> currentPlayer().noScores() &&
0172         KMessageBox::warningContinueCancel(
0173             this,
0174             i18n("Starting the demo will end the current game. Any progress will be lost. Do you want to continue?")
0175         ) == KMessageBox::Cancel
0176     )
0177     {
0178         m_demoAction -> setChecked(false);
0179         return;
0180     }
0181 
0182     bool preDemoHumans[6];
0183     preDemoHumans[0] = kirikiSettings::player1IsHuman();
0184     preDemoHumans[1] = kirikiSettings::player2IsHuman();
0185     preDemoHumans[2] = kirikiSettings::player3IsHuman();
0186     preDemoHumans[3] = kirikiSettings::player4IsHuman();
0187     preDemoHumans[4] = kirikiSettings::player5IsHuman();
0188     preDemoHumans[5] = kirikiSettings::player6IsHuman();
0189     int preDemoNumPlayers = kirikiSettings::numberOfPlayers();
0190     kirikiSettings::setPlayer1IsHuman(false);
0191     kirikiSettings::setPlayer2IsHuman(false);
0192     kirikiSettings::setPlayer3IsHuman(false);
0193     kirikiSettings::setPlayer4IsHuman(false);
0194     kirikiSettings::setPlayer5IsHuman(false);
0195     kirikiSettings::setPlayer6IsHuman(false);
0196     kirikiSettings::setNumberOfPlayers(6);
0197     newGame();
0198     Q_EMIT demoStarted();
0199     disconnect(m_lateral, &lateralWidget::rolled, statusBar(), &QWidget::hide);
0200     kirikiSettings::setPlayer1IsHuman(preDemoHumans[0]);
0201     kirikiSettings::setPlayer2IsHuman(preDemoHumans[1]);
0202     kirikiSettings::setPlayer3IsHuman(preDemoHumans[2]);
0203     kirikiSettings::setPlayer4IsHuman(preDemoHumans[3]);
0204     kirikiSettings::setPlayer5IsHuman(preDemoHumans[4]);
0205     kirikiSettings::setPlayer6IsHuman(preDemoHumans[5]);
0206     kirikiSettings::setNumberOfPlayers(preDemoNumPlayers);
0207     statusBar()->showMessage(i18n("Demonstration. Press \"New\" to start a new game."));
0208     statusBar()->show();
0209     m_lateral->enableDemoMode();
0210 }
0211 
0212 void kiriki::showHint()
0213 {
0214     if (!m_hintGiven && KMessageBox::Cancel == KMessageBox::warningContinueCancel(
0215             this,
0216             i18n("Asking for a hint will disqualify the current game from entering the high score list."),
0217             i18n("Confirm Hint Request"),
0218             KGuiItem(i18n("Give Hint Anyway"), QStringLiteral("arrow-right"))
0219             )
0220         ) return;
0221     m_hintGiven = true;
0222 
0223     for (int i = 0; i < 5; ++i) setComputerDiceValue(i, m_lateral -> getDice(i));
0224     ComputerRolling(m_scores -> currentPlayer(), m_lateral -> getRolls());
0225     if (computerDiceSelected() && m_lateral -> getRolls() < 3)
0226     {
0227         int rollAmount = 0;
0228         for (int i = 0; i < 5; ++i)
0229         {
0230             if (getComputerDiceSelected(i))
0231             {
0232                 ++rollAmount;
0233                 m_lateral -> highlightDice(i, true);
0234             }
0235         }
0236         m_scoresWidget->selectionModel()->clearSelection();
0237         statusBar()->showMessage(i18np("Roll highlighted die.", "Roll highlighted dice.", rollAmount));
0238         statusBar()->show();
0239     }
0240     else
0241     {
0242         static const int scoreRows[13] = { 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16 };
0243         QModelIndex mi;
0244         mi = m_scores -> index(scoreRows[ComputerScoring(m_scores -> currentPlayer())], 0);
0245         m_highlightedRowIndex  = mi.row();
0246         m_scoresWidget -> setItemDelegateForRow(m_highlightedRowIndex, m_delegateHighlighted);
0247         QItemSelectionModel::SelectionFlags fl = QItemSelectionModel::Clear |
0248                                                  QItemSelectionModel::Rows  |
0249                                                  QItemSelectionModel::Select;
0250         m_scoresWidget -> selectionModel() -> select(mi, fl);
0251     }
0252 }
0253 
0254 void kiriki::settingsChanged()
0255 {
0256     if (m_fontSize != kirikiSettings::fontSize())
0257     {
0258         m_scoresWidget -> resizeColumnToContents(0);
0259         m_scores->askForRedraw();
0260         m_fontSize = kirikiSettings::fontSize();
0261     }
0262 
0263     if (m_rowHeight != kirikiSettings::rowHeight())
0264     {
0265         m_rowHeight = kirikiSettings::rowHeight();
0266         m_itemDelegate -> setHeight(m_rowHeight);
0267         m_scores -> askForRedraw();
0268     }
0269 }
0270 
0271 void kiriki::endGame()
0272 {
0273     const player &p = m_scores -> winner();
0274     m_lateral -> setEnabled(false);
0275     m_lateral -> endGame();
0276     m_hintAction -> setEnabled(false);
0277     if (p.isHuman())
0278     {
0279         QPointer<KScoreDialog> sc = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Date, this);
0280         if (m_hintGiven) m_hintGiven = false;
0281         else
0282         {
0283             KScoreDialog::FieldInfo scoreInfo;
0284             scoreInfo[KScoreDialog::Score].setNum(p.grandTotal());
0285             const QDate date = QDate::currentDate();
0286                         QLocale l;
0287                         const QString datestring = l.toString(date, QLocale::ShortFormat);
0288             scoreInfo[KScoreDialog::Date] = datestring;
0289             
0290             if (sc->addScore(scoreInfo))
0291             {
0292                 sc->exec();
0293             }
0294         }
0295         delete sc;
0296     }
0297     if (m_demoAction -> isChecked()) QTimer::singleShot(3000, this, &kiriki::demo);
0298 }
0299 
0300 void kiriki::showHighScores()
0301 {
0302     KScoreDialog sc(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Date, this);
0303     sc.exec();
0304 }
0305 
0306 void kiriki::showPreferences()
0307 {
0308     if(KConfigDialog::showDialog(QStringLiteral("settings")))
0309         return;
0310 
0311     int nPlayers = kirikiSettings::numberOfPlayers();
0312     QString player1Name = kirikiSettings::player1Name();
0313     QString player2Name = kirikiSettings::player2Name();
0314     QString player3Name = kirikiSettings::player3Name();
0315     QString player4Name = kirikiSettings::player4Name();
0316     QString player5Name = kirikiSettings::player5Name();
0317     QString player6Name = kirikiSettings::player6Name();
0318     bool player2IsHuman = kirikiSettings::player2IsHuman();
0319     bool player3IsHuman = kirikiSettings::player3IsHuman();
0320     bool player4IsHuman = kirikiSettings::player4IsHuman();
0321     bool player5IsHuman = kirikiSettings::player5IsHuman();
0322     bool player6IsHuman = kirikiSettings::player6IsHuman();
0323     m_fontSize = kirikiSettings::fontSize();
0324     m_rowHeight = kirikiSettings::rowHeight();
0325     
0326     QPointer<KConfigDialog> configDialog = new KConfigDialog(this, QStringLiteral("settings"), kirikiSettings::self());
0327     configDialog->setFaceType(KConfigDialog::Plain);
0328     configDialog->addPage(new configWidget(configDialog), QString(), QString());
0329     connect(configDialog.data(), &KConfigDialog::settingsChanged, this, &kiriki::settingsChanged);
0330     configDialog->exec();
0331     delete configDialog;
0332 
0333     bool changed = nPlayers != kirikiSettings::numberOfPlayers() ||
0334                    player1Name != kirikiSettings::player1Name() || 
0335                    player2Name != kirikiSettings::player2Name() || 
0336                    player3Name != kirikiSettings::player3Name() || 
0337                    player4Name != kirikiSettings::player4Name() || 
0338                    player5Name != kirikiSettings::player5Name() || 
0339                    player6Name != kirikiSettings::player6Name() || 
0340                player2IsHuman != kirikiSettings::player2IsHuman() ||
0341                player3IsHuman != kirikiSettings::player3IsHuman() ||
0342                player4IsHuman != kirikiSettings::player4IsHuman() ||
0343                player5IsHuman != kirikiSettings::player5IsHuman() ||
0344                player6IsHuman != kirikiSettings::player6IsHuman();
0345     if (changed)
0346     {
0347         KMessageBox::information(this, i18n("Changes will be applied on next game."));
0348     }
0349 }
0350 
0351 void kiriki::print()
0352 {
0353     QPrinter printer;
0354     printer.setFullPage( true );
0355     QPointer<QPrintDialog> printDialog = new QPrintDialog(&printer, this);
0356     if (printDialog->exec())
0357     {
0358         QPainter painter(&printer);
0359         m_scores->print(painter, printer.width(), printer.height());
0360     }
0361     delete printDialog;
0362 }
0363 
0364 void kiriki::nextTurn()
0365 {
0366     if (m_scores -> allScores()) endGame();
0367     else
0368     {
0369         m_scores -> nextPlayer();
0370 
0371         while (m_scores -> currentPlayer().allScores()) m_scores -> nextPlayer();
0372         
0373         m_lateral -> nextTurn();
0374         if (!m_scores -> currentPlayer().isHuman())
0375         {
0376             m_hintAction -> setEnabled(false);
0377             QTimer::singleShot(kirikiSettings::waitTime(), this, &kiriki::playComputer);
0378         }
0379         else
0380         {
0381             m_hintAction -> setEnabled(true);
0382             m_lateral -> setEnabled(true);
0383         }
0384      }
0385 }
0386 
0387 void kiriki::playComputer()
0388 {
0389     if (!m_scores -> currentPlayer().isHuman())
0390     {
0391     m_lateral -> setEnabled(false);
0392     for (int i = 0; i < 5; i++) setComputerDiceValue(i, m_lateral -> getDice(i));
0393     ComputerRolling(m_scores -> currentPlayer(), m_lateral -> getRolls());
0394     while (computerDiceSelected() && m_lateral -> getRolls() < 3)
0395     {
0396         for (int i = 0; i < 5; i++) m_lateral -> selectDice(i, getComputerDiceSelected(i));
0397         m_lateral -> roll();
0398         for (int i = 0; i < 5; i++) setComputerDiceValue(i, m_lateral -> getDice(i));
0399         ComputerRolling(m_scores -> currentPlayer(), m_lateral -> getRolls());
0400     }
0401     int row;
0402     row = ComputerScoring(m_scores -> currentPlayer());
0403     play(row);
0404     }
0405     else m_lateral -> setEnabled(true);
0406 }
0407 
0408