Warning, file /games/killbots/src/mainwindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     This file is part of Killbots.
0003 
0004     SPDX-FileCopyrightText: 2006-2009 Parker Coates <coates@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "mainwindow.h"
0010 
0011 #include "coordinator.h"
0012 #include "engine.h"
0013 #include "optionspage.h"
0014 #include "renderer.h"
0015 #include "rulesetselector.h"
0016 #include "scene.h"
0017 #include "settings.h"
0018 #include "view.h"
0019 
0020 #include <KgThemeProvider>
0021 #include <KgThemeSelector>
0022 #include <highscore/kscoredialog.h>
0023 #include <kstandardgameaction.h>
0024 
0025 #include <QAction>
0026 #include <kwidgetsaddons_version.h>
0027 #include <KActionCollection>
0028 #include <KConfigDialog>
0029 #include <KLocalizedString>
0030 #include <KMessageBox>
0031 #include <KShortcutsDialog>
0032 #include <KStandardAction>
0033 
0034 #include <QDir>
0035 #include <QIcon>
0036 #include <QTimer>
0037 
0038 #include "killbots_debug.h"
0039 
0040 Killbots::MainWindow::MainWindow(QWidget *parent)
0041     : KXmlGuiWindow(parent),
0042       m_scoreDialog(nullptr),
0043       m_rulesetChanged(false)
0044 {
0045     setAcceptDrops(false);
0046 
0047     m_coordinator = new Coordinator(this);
0048     m_coordinator->setAnimationSpeed(Settings::animationSpeed());
0049 
0050     m_engine = new Engine(m_coordinator, this);
0051     m_coordinator->setEngine(m_engine);
0052     connect(m_engine, &Engine::gameOver, this, &MainWindow::onGameOver);
0053 
0054     m_scene = new Scene(this);
0055     m_coordinator->setScene(m_scene);
0056     connect(m_scene, &Scene::clicked, m_coordinator, &Coordinator::requestAction);
0057     connect(Renderer::self()->themeProvider(), &KgThemeProvider::currentThemeChanged, m_scene, &Scene::doLayout);
0058 
0059     m_view = new View(m_scene, this);
0060     m_view->setMinimumSize(400, 280);
0061     m_view->setWhatsThis(i18n("This is the main game area used to interact with Killbots. It shows the current state of the game grid and allows one to control the hero using the mouse. It also displays certain statistics about the game in progress."));
0062     setCentralWidget(m_view);
0063     connect(m_view, &View::sizeChanged, m_scene, &Scene::doLayout);
0064 
0065     setupActions();
0066     connect(m_engine, &Engine::teleportAllowed,       actionCollection()->action(QStringLiteral("teleport")),        &QAction::setEnabled);
0067     connect(m_engine, &Engine::teleportAllowed,       actionCollection()->action(QStringLiteral("teleport_sip")),    &QAction::setEnabled);
0068     connect(m_engine, &Engine::teleportSafelyAllowed, actionCollection()->action(QStringLiteral("teleport_safely")), &QAction::setEnabled);
0069     connect(m_engine, &Engine::vaporizerAllowed,      actionCollection()->action(QStringLiteral("vaporizer")),       &QAction::setEnabled);
0070     connect(m_engine, &Engine::waitOutRoundAllowed,   actionCollection()->action(QStringLiteral("wait_out_round")),  &QAction::setEnabled);
0071 
0072     setupGUI(Save | Create | ToolBar | Keys);
0073 
0074     // Delaying the start of the first game by 50ms to avoid resize events after
0075     // the game has been started. Delaying by 0ms doesn't seem to be enough.
0076     QTimer::singleShot(50, m_coordinator, &Coordinator::requestNewGame);
0077 }
0078 
0079 Killbots::MainWindow::~MainWindow()
0080 {
0081     Killbots::Renderer::cleanup();
0082 }
0083 
0084 void Killbots::MainWindow::configurePreferences()
0085 {
0086     // An instance of the dialog could be already created and could be cached,
0087     // in which case we want to display the cached dialog instead of creating
0088     // another one
0089     if (!KConfigDialog::showDialog(QStringLiteral("configurePreferencesDialog"))) {
0090         // Creating a dialog because KConfigDialog didn't find an instance of it
0091         KConfigDialog *configDialog = new KConfigDialog(this, QStringLiteral("configurePreferencesDialog"), Settings::self());
0092 
0093         // Creating setting pages and adding them to the dialog
0094         configDialog->addPage(new OptionsPage(this),
0095                               i18nc("@title", "General"),
0096                               QStringLiteral("configure"),
0097                               i18nc("@info", "Configure general settings")
0098                              );
0099         configDialog->addPage(new RulesetSelector(this),
0100                               i18nc("@title", "Game Type"),
0101                               QStringLiteral("games-config-custom"),
0102                               i18nc("@info", "Select a game type")
0103                              );
0104         configDialog->addPage(new KgThemeSelector(Renderer::self()->themeProvider()),
0105                               i18nc("@title", "Appearance"),
0106                               QStringLiteral("games-config-theme"),
0107                               i18nc("@info", "Select a graphical theme")
0108                              );
0109 
0110         configDialog->setMaximumSize(800, 600);
0111 
0112         // Update the sprite style if it has changed
0113         connect(configDialog, &KConfigDialog::settingsChanged, this, &MainWindow::onSettingsChanged);
0114 
0115         configDialog->show();
0116     }
0117 }
0118 
0119 void Killbots::MainWindow::onSettingsChanged()
0120 {
0121     m_coordinator->setAnimationSpeed(Settings::animationSpeed());
0122 
0123     if (m_engine->ruleset()->fileName() != Settings::ruleset()) {
0124         qCDebug(KILLBOTS_LOG) << "Detected a changed in ruleset. From" << m_engine->ruleset()->fileName() << "to" << Settings::ruleset();
0125 
0126         // We don't act on the changed ruleset here because the config dialog
0127         // is still visible. We want the game in progress to be visible when
0128         // we display the message box. We also don't want a new game to start
0129         // while hidden behind the config dialog. So we wait until the config
0130         // dialog is closed. See onConfigDialogClosed below.
0131         m_rulesetChanged = true;
0132     }
0133     onConfigDialogClosed();
0134 }
0135 
0136 void Killbots::MainWindow::onConfigDialogClosed()
0137 {
0138     if (m_rulesetChanged) {
0139         if (!m_engine->gameHasStarted()
0140 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0141                 || KMessageBox::questionTwoActions(this,
0142 #else
0143                 || KMessageBox::questionYesNo(this,
0144 #endif
0145                                               i18n("A new game type has been selected, but there is already a game in progress."),
0146                                               i18nc("@title:window", "Game Type Changed"),
0147                                               KGuiItem(i18nc("@action:button", "Continue Current Game")),
0148                                               KGuiItem(i18nc("@action:button", "Start a New Game"))
0149 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0150                                              ) == KMessageBox::SecondaryAction
0151 #else
0152                                              ) == KMessageBox::No
0153 #endif
0154            ) {
0155             m_coordinator->requestNewGame();
0156         }
0157 
0158         m_rulesetChanged = false;
0159     }
0160 }
0161 
0162 void Killbots::MainWindow::createScoreDialog()
0163 {
0164     m_scoreDialog = new KScoreDialog(KScoreDialog::Name, this);
0165     m_scoreDialog->addField(KScoreDialog::Level, i18nc("@title:column round of the game", "Round"), QStringLiteral("round"));
0166     m_scoreDialog->setModal(false);
0167     QStringList fileList;
0168     const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("killbots/ruleset"), QStandardPaths::LocateDirectory);
0169     for (const QString &dir : dirs) {
0170         const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.desktop"));
0171         for (const QString &file : fileNames) {
0172             fileList.append(dir + QLatin1Char('/') + file);
0173         }
0174     }
0175     for (const QString &fileName : std::as_const(fileList)) {
0176         const Ruleset *ruleset = Ruleset::load(fileName);
0177         if (ruleset) {
0178             m_scoreDialog->addLocalizedConfigGroupName(qMakePair(ruleset->scoreGroupKey(), ruleset->name()));
0179         }
0180         delete ruleset;
0181     }
0182 }
0183 
0184 void Killbots::MainWindow::onGameOver(int score, int round)
0185 {
0186     if (score && m_engine->ruleset()) {
0187         if (!m_scoreDialog) {
0188             createScoreDialog();
0189         }
0190 
0191         m_scoreDialog->setConfigGroup(qMakePair(m_engine->ruleset()->scoreGroupKey(), m_engine->ruleset()->name()));
0192 
0193         KScoreDialog::FieldInfo scoreEntry;
0194         scoreEntry[ KScoreDialog::Score ].setNum(score);
0195         scoreEntry[ KScoreDialog::Level ].setNum(round);
0196 
0197         if (m_scoreDialog->addScore(scoreEntry)) {
0198             QTimer::singleShot(1000, this, &MainWindow::showHighscores);
0199         }
0200     }
0201 }
0202 
0203 void Killbots::MainWindow::showHighscores()
0204 {
0205     if (!m_scoreDialog) {
0206         createScoreDialog();
0207     }
0208 
0209     // TODO: Find out why this line breaks adding high scores.
0210     //m_scoreDialog->setConfigGroup(qMakePair(m_engine->ruleset()->scoreGroupKey(), m_engine->ruleset()->name()));
0211     m_scoreDialog->exec();
0212 }
0213 
0214 QAction *Killbots::MainWindow::createMappedAction(int mapping,
0215         const QString &internalName,
0216         const QString &displayName,
0217         const QString &translatedShortcut,
0218         const int alternateShortcut,
0219         const QString &helpText,
0220         const QString &icon
0221                                                  )
0222 {
0223     QAction *action = new QAction(displayName, actionCollection());
0224     action->setObjectName(internalName);
0225     actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>() << QKeySequence(translatedShortcut) << QKeySequence(alternateShortcut) << QKeySequence(alternateShortcut | Qt::KeypadModifier));
0226     if (!helpText.isEmpty()) {
0227         action->setWhatsThis(helpText);
0228         action->setToolTip(helpText);
0229     }
0230     if (!icon.isEmpty()) {
0231         action->setIcon(QIcon::fromTheme(icon));
0232     }
0233 
0234     connect(action, &QAction::triggered, this, [this, mapping] { m_coordinator->requestAction(mapping); });
0235     actionCollection()->addAction(internalName, action);
0236 
0237     return action;
0238 }
0239 
0240 void Killbots::MainWindow::setupActions()
0241 {
0242     KStandardGameAction::gameNew(m_coordinator, &Coordinator::requestNewGame, actionCollection());
0243     KStandardGameAction::highscores(this, &MainWindow::showHighscores, actionCollection());
0244     KStandardGameAction::quit(qApp, &QCoreApplication::quit, actionCollection());
0245     KStandardAction::preferences(this, &MainWindow::configurePreferences, actionCollection());
0246 
0247     createMappedAction(TeleportSafely,
0248                        QStringLiteral("teleport_safely"),
0249                        i18nc("@action", "Teleport Safely"),
0250                        i18nc("Shortcut for teleport safely. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "T"),
0251                        Qt::Key_Plus,
0252                        i18nc("@info:tooltip", "Teleport to a safe location"),
0253                        QStringLiteral("games-solve")
0254                       );
0255     createMappedAction(Teleport,
0256                        QStringLiteral("teleport"),
0257                        i18nc("@action", "Teleport"),
0258                        i18nc("Shortcut for teleport. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "R"),
0259                        Qt::Key_Minus,
0260                        i18nc("@info:tooltip", "Teleport to a random location"),
0261                        QStringLiteral("roll")
0262                       );
0263     createMappedAction(TeleportSafelyIfPossible,
0264                        QStringLiteral("teleport_sip"),
0265                        i18nc("@action", "Teleport (Safely if Possible)"),
0266                        i18nc("Shortcut for teleport safely if possible. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "Space"),
0267                        Qt::Key_0,
0268                        i18nc("@info:tooltip", "Teleport safely if that action is enabled, otherwise teleport randomly")
0269                       );
0270     createMappedAction(Vaporizer,
0271                        QStringLiteral("vaporizer"),
0272                        i18nc("@action", "Vaporizer"),
0273                        i18nc("Shortcut for vaporizer. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "F"),
0274                        Qt::Key_Period,
0275                        i18nc("@info:tooltip", "Destroy all enemies in neighboring cells"),
0276                        QStringLiteral("edit-bomb")
0277                       );
0278     createMappedAction(WaitOutRound,
0279                        QStringLiteral("wait_out_round"),
0280                        i18nc("@action", "Wait Out Round"),
0281                        i18nc("Shortcut for wait out round. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "V"),
0282                        Qt::Key_Asterisk,
0283                        i18nc("@info:tooltip", "Risk remaining in place until the end of the round for bonuses"),
0284                        QStringLiteral("process-stop")
0285                       );
0286     createMappedAction(UpLeft,
0287                        QStringLiteral("move_up_left"),
0288                        i18nc("@action", "Move Up and Left"),
0289                        i18nc("Shortcut for move up and left. https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "Q"),
0290                        Qt::Key_7
0291                       );
0292     createMappedAction(Up,
0293                        QStringLiteral("move_up"),
0294                        i18nc("@action", "Move Up"),
0295                        i18nc("Shortcut for move up. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "W"),
0296                        Qt::Key_8
0297                       );
0298     createMappedAction(UpRight,
0299                        QStringLiteral("move_up_right"),
0300                        i18nc("@action", "Move Up and Right"),
0301                        i18nc("Shortcut for move up and right. https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "E"),
0302                        Qt::Key_9
0303                       );
0304     createMappedAction(Left,
0305                        QStringLiteral("move_left"),
0306                        i18nc("@action", "Move Left"),
0307                        i18nc("Shortcut for move left. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "A"),
0308                        Qt::Key_4
0309                       );
0310     createMappedAction(Hold,
0311                        QStringLiteral("stand_still"),
0312                        i18nc("@action", "Stand Still"),
0313                        i18nc("Shortcut for stand still. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "S"),
0314                        Qt::Key_5
0315                       );
0316     createMappedAction(Right,
0317                        QStringLiteral("move_right"),
0318                        i18nc("@action", "Move Right"),
0319                        i18nc("Shortcut for move right. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "D"),
0320                        Qt::Key_6
0321                       );
0322     createMappedAction(DownLeft,
0323                        QStringLiteral("move_down_left"),
0324                        i18nc("@action", "Move Down and Left"),
0325                        i18nc("Shortcut for move down and left. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "Z"),
0326                        Qt::Key_1
0327                       );
0328     createMappedAction(Down,
0329                        QStringLiteral("move_down"),
0330                        i18nc("@action", "Move Down"),
0331                        i18nc("Shortcut for move down. See https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "X"),
0332                        Qt::Key_2
0333                       );
0334     createMappedAction(DownRight,
0335                        QStringLiteral("move_down_right"),
0336                        i18nc("@action", "Move Down and Right"),
0337                        i18nc("Shortcut for move down and right. https://quickgit.kde.org/?p=killbots.git&a=blob&f=README.translators&o=plain", "C"),
0338                        Qt::Key_3
0339                       );
0340 }
0341 
0342 #include "moc_mainwindow.cpp"