File indexing completed on 2024-05-12 08:02:10
0001 /* 0002 SPDX-FileCopyrightText: 2007 Nicolas Roffet <nicolas-kde@roffet.com> 0003 SPDX-FileCopyrightText: 2007 Pino Toscano <toscano.pino@tiscali.it> 0004 SPDX-FileCopyrightText: 2011-2012 Stefan Majewsky <majewsky@gmx.net> 0005 0006 SPDX-License-Identifier: LGPL-2.0-only 0007 */ 0008 0009 #include "kgamedifficulty.h" 0010 0011 // KF 0012 #include <KActionCollection> 0013 #include <KComboBox> 0014 #include <KConfigGroup> 0015 #include <KGuiItem> 0016 #include <KLocalizedString> 0017 #include <KMessageBox> 0018 #include <KSelectAction> 0019 #include <KSharedConfig> 0020 #include <KXmlGuiWindow> 0021 // Qt 0022 #include <QCoreApplication> 0023 #include <QIcon> 0024 #include <QList> 0025 #include <QStatusBar> 0026 // Std 0027 #include <algorithm> 0028 #include <utility> 0029 0030 // BEGIN KGameDifficultyLevel 0031 0032 class KGameDifficultyLevelPrivate 0033 { 0034 public: 0035 bool m_isDefault; 0036 int m_hardness; 0037 KGameDifficultyLevel::StandardLevel m_level; 0038 QByteArray m_key; 0039 QString m_title; 0040 0041 public: 0042 KGameDifficultyLevelPrivate(int hardness, const QByteArray &key, const QString &title, KGameDifficultyLevel::StandardLevel level, bool isDefault); 0043 static KGameDifficultyLevelPrivate *fromStandardLevel(KGameDifficultyLevel::StandardLevel level, bool isDefault); 0044 }; 0045 0046 KGameDifficultyLevel::KGameDifficultyLevel(int hardness, const QByteArray &key, const QString &title, bool isDefault) 0047 : d_ptr(new KGameDifficultyLevelPrivate(hardness, key, title, Custom, isDefault)) 0048 { 0049 } 0050 0051 KGameDifficultyLevelPrivate::KGameDifficultyLevelPrivate(int hardness, 0052 const QByteArray &key, 0053 const QString &title, 0054 KGameDifficultyLevel::StandardLevel level, 0055 bool isDefault) 0056 : m_isDefault(isDefault) 0057 , m_hardness(hardness) 0058 , m_level(level) 0059 , m_key(key) 0060 , m_title(title) 0061 { 0062 } 0063 0064 KGameDifficultyLevel::KGameDifficultyLevel(StandardLevel level, bool isDefault) 0065 : d_ptr(KGameDifficultyLevelPrivate::fromStandardLevel(level, isDefault)) 0066 { 0067 } 0068 0069 KGameDifficultyLevelPrivate *KGameDifficultyLevelPrivate::fromStandardLevel(KGameDifficultyLevel::StandardLevel level, bool isDefault) 0070 { 0071 Q_ASSERT_X(level != KGameDifficultyLevel::Custom, "KGameDifficultyLevel(StandardLevel) constructor", "Custom level not allowed here"); 0072 // The first entry in the pair is to be used as a key so don't change it. It doesn't have to match the string to be translated 0073 QPair<QByteArray, QString> data; 0074 switch (level) { 0075 case KGameDifficultyLevel::RidiculouslyEasy: 0076 data = qMakePair(QByteArrayLiteral("Ridiculously Easy"), i18nc("Game difficulty level 1 out of 8", "Ridiculously Easy")); 0077 break; 0078 case KGameDifficultyLevel::VeryEasy: 0079 data = qMakePair(QByteArrayLiteral("Very Easy"), i18nc("Game difficulty level 2 out of 8", "Very Easy")); 0080 break; 0081 case KGameDifficultyLevel::Easy: 0082 data = qMakePair(QByteArrayLiteral("Easy"), i18nc("Game difficulty level 3 out of 8", "Easy")); 0083 break; 0084 case KGameDifficultyLevel::Medium: 0085 data = qMakePair(QByteArrayLiteral("Medium"), i18nc("Game difficulty level 4 out of 8", "Medium")); 0086 break; 0087 case KGameDifficultyLevel::Hard: 0088 data = qMakePair(QByteArrayLiteral("Hard"), i18nc("Game difficulty level 5 out of 8", "Hard")); 0089 break; 0090 case KGameDifficultyLevel::VeryHard: 0091 data = qMakePair(QByteArrayLiteral("Very Hard"), i18nc("Game difficulty level 6 out of 8", "Very Hard")); 0092 break; 0093 case KGameDifficultyLevel::ExtremelyHard: 0094 data = qMakePair(QByteArrayLiteral("Extremely Hard"), i18nc("Game difficulty level 7 out of 8", "Extremely Hard")); 0095 break; 0096 case KGameDifficultyLevel::Impossible: 0097 data = qMakePair(QByteArrayLiteral("Impossible"), i18nc("Game difficulty level 8 out of 8", "Impossible")); 0098 break; 0099 case KGameDifficultyLevel::Custom: 0100 return nullptr; 0101 } 0102 return new KGameDifficultyLevelPrivate(level, data.first, data.second, level, isDefault); 0103 } 0104 0105 KGameDifficultyLevel::~KGameDifficultyLevel() = default; 0106 0107 bool KGameDifficultyLevel::isDefault() const 0108 { 0109 Q_D(const KGameDifficultyLevel); 0110 0111 return d->m_isDefault; 0112 } 0113 0114 int KGameDifficultyLevel::hardness() const 0115 { 0116 Q_D(const KGameDifficultyLevel); 0117 0118 return d->m_hardness; 0119 } 0120 0121 QByteArray KGameDifficultyLevel::key() const 0122 { 0123 Q_D(const KGameDifficultyLevel); 0124 0125 return d->m_key; 0126 } 0127 0128 QString KGameDifficultyLevel::title() const 0129 { 0130 Q_D(const KGameDifficultyLevel); 0131 0132 return d->m_title; 0133 } 0134 0135 KGameDifficultyLevel::StandardLevel KGameDifficultyLevel::standardLevel() const 0136 { 0137 Q_D(const KGameDifficultyLevel); 0138 0139 return d->m_level; 0140 } 0141 0142 // END KGameDifficultyLevel 0143 // BEGIN KGameDifficulty 0144 0145 class KGameDifficultyPrivate 0146 { 0147 public: 0148 QList<const KGameDifficultyLevel *> m_levels; 0149 mutable const KGameDifficultyLevel *m_currentLevel = nullptr; 0150 bool m_editable = true; 0151 bool m_gameRunning = false; 0152 0153 public: 0154 KGameDifficultyPrivate() = default; 0155 }; 0156 0157 static void saveLevel() 0158 { 0159 // save current difficulty level in config file (no sync() call here; this 0160 // will most likely be called at application shutdown when others are also 0161 // writing to KGlobal::config(); also KConfig's dtor will sync automatically) 0162 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KgDifficulty")); 0163 cg.writeEntry("Level", KGameDifficulty::global()->currentLevel()->key()); 0164 } 0165 0166 KGameDifficulty::KGameDifficulty(QObject *parent) 0167 : QObject(parent) 0168 , d_ptr(new KGameDifficultyPrivate) 0169 { 0170 qRegisterMetaType<const KGameDifficultyLevel *>(); 0171 qAddPostRoutine(saveLevel); 0172 } 0173 0174 KGameDifficulty::~KGameDifficulty() 0175 { 0176 Q_D(KGameDifficulty); 0177 0178 qDeleteAll(d->m_levels); 0179 } 0180 0181 void KGameDifficulty::addLevel(KGameDifficultyLevel *level) 0182 { 0183 Q_D(KGameDifficulty); 0184 0185 // The intended use is to create the KGameDifficulty object, add levels, *then* 0186 // start to work with the currentLevel(). The first call to currentLevel() 0187 // will load the previous selection from the config, and the level list will 0188 // be considered immutable from this point. 0189 Q_ASSERT_X(d->m_currentLevel == nullptr, "KGameDifficulty::addLevel", "Only allowed before currentLevel() is called."); 0190 // ensure that list stays sorted 0191 const int newLevelHardness = level->hardness(); 0192 auto it = std::find_if(d->m_levels.begin(), d->m_levels.end(), [newLevelHardness](const KGameDifficultyLevel *l) { 0193 return l->hardness() >= newLevelHardness; 0194 }); 0195 d->m_levels.insert(it, level); 0196 level->setParent(this); 0197 } 0198 0199 typedef KGameDifficultyLevel::StandardLevel DS; 0200 0201 void KGameDifficulty::addStandardLevel(DS level, bool isDefault) 0202 { 0203 addLevel(new KGameDifficultyLevel(level, isDefault)); 0204 } 0205 0206 void KGameDifficulty::addStandardLevelRange(DS from, DS to) 0207 { 0208 // every level in range != Custom, therefore no level is default 0209 addStandardLevelRange(from, to, KGameDifficultyLevel::Custom); 0210 } 0211 0212 void KGameDifficulty::addStandardLevelRange(DS from, DS to, DS defaultLevel) 0213 { 0214 const QList<DS> levels{ 0215 KGameDifficultyLevel::RidiculouslyEasy, 0216 KGameDifficultyLevel::VeryEasy, 0217 KGameDifficultyLevel::Easy, 0218 KGameDifficultyLevel::Medium, 0219 KGameDifficultyLevel::Hard, 0220 KGameDifficultyLevel::VeryHard, 0221 KGameDifficultyLevel::ExtremelyHard, 0222 KGameDifficultyLevel::Impossible, 0223 }; 0224 const int fromIndex = levels.indexOf(from); 0225 const int toIndex = levels.indexOf(to); 0226 const int defaultLevelIndex = levels.indexOf(defaultLevel); 0227 Q_ASSERT_X(fromIndex >= 0 && toIndex > fromIndex 0228 && (defaultLevelIndex == KGameDifficultyLevel::Custom || (defaultLevelIndex >= fromIndex && defaultLevelIndex <= toIndex)), 0229 "KGameDifficulty::addStandardLevelRange", 0230 "No argument may be KGameDifficultyLevel::Custom."); 0231 for (int i = fromIndex; i <= toIndex; ++i) { 0232 addLevel(new KGameDifficultyLevel(levels[i], levels[i] == defaultLevel)); 0233 } 0234 } 0235 0236 QList<const KGameDifficultyLevel *> KGameDifficulty::levels() const 0237 { 0238 Q_D(const KGameDifficulty); 0239 0240 return d->m_levels; 0241 } 0242 0243 const KGameDifficultyLevel *KGameDifficulty::currentLevel() const 0244 { 0245 Q_D(const KGameDifficulty); 0246 0247 if (d->m_currentLevel) { 0248 return d->m_currentLevel; 0249 } 0250 Q_ASSERT(!d->m_levels.isEmpty()); 0251 // check configuration file for saved difficulty level 0252 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KgDifficulty")); 0253 const QByteArray key = cg.readEntry("Level", QByteArray()); 0254 for (const KGameDifficultyLevel *level : std::as_const(d->m_levels)) { 0255 if (level->key() == key) { 0256 return d->m_currentLevel = level; 0257 } 0258 } 0259 // no level predefined - look for a default level 0260 for (const KGameDifficultyLevel *level : std::as_const(d->m_levels)) { 0261 if (level->isDefault()) { 0262 return d->m_currentLevel = level; 0263 } 0264 } 0265 // no default level predefined - easiest level is probably a sane default 0266 return d->m_currentLevel = d->m_levels[0]; 0267 } 0268 0269 bool KGameDifficulty::isEditable() const 0270 { 0271 Q_D(const KGameDifficulty); 0272 0273 return d->m_editable; 0274 } 0275 0276 void KGameDifficulty::setEditable(bool editable) 0277 { 0278 Q_D(KGameDifficulty); 0279 0280 if (d->m_editable == editable) { 0281 return; 0282 } 0283 d->m_editable = editable; 0284 Q_EMIT editableChanged(editable); 0285 } 0286 0287 bool KGameDifficulty::isGameRunning() const 0288 { 0289 Q_D(const KGameDifficulty); 0290 0291 return d->m_gameRunning; 0292 } 0293 0294 void KGameDifficulty::setGameRunning(bool gameRunning) 0295 { 0296 Q_D(KGameDifficulty); 0297 0298 if (d->m_gameRunning == gameRunning) { 0299 return; 0300 } 0301 d->m_gameRunning = gameRunning; 0302 Q_EMIT gameRunningChanged(gameRunning); 0303 } 0304 0305 void KGameDifficulty::select(const KGameDifficultyLevel *level) 0306 { 0307 Q_D(KGameDifficulty); 0308 0309 Q_ASSERT(d->m_levels.contains(level)); 0310 if (d->m_currentLevel == level) { 0311 return; 0312 } 0313 // ask for confirmation if necessary 0314 if (d->m_gameRunning) { 0315 const int result = KMessageBox::warningContinueCancel(nullptr, 0316 i18n("Changing the difficulty level will end the current game!"), 0317 QString(), 0318 KGuiItem(i18n("Change the difficulty level"))); 0319 if (result != KMessageBox::Continue) { 0320 Q_EMIT selectedLevelChanged(d->m_currentLevel); 0321 return; 0322 } 0323 } 0324 d->m_currentLevel = level; 0325 Q_EMIT selectedLevelChanged(level); 0326 Q_EMIT currentLevelChanged(level); 0327 } 0328 0329 // END KGameDifficulty 0330 0331 Q_GLOBAL_STATIC(KGameDifficulty, g_difficulty) 0332 0333 KGameDifficulty *KGameDifficulty::global() 0334 { 0335 return g_difficulty; 0336 } 0337 0338 KGameDifficultyLevel::StandardLevel KGameDifficulty::globalLevel() 0339 { 0340 return g_difficulty->currentLevel()->standardLevel(); 0341 } 0342 0343 // BEGIN KGameDifficultyGUI 0344 0345 namespace KGameDifficultyGUI 0346 { 0347 class Selector : public KComboBox 0348 { 0349 Q_OBJECT 0350 0351 private: 0352 KGameDifficulty *d; 0353 0354 public: 0355 Selector(KGameDifficulty *difficulty, QWidget *parent = nullptr) 0356 : KComboBox(parent) 0357 , d(difficulty) 0358 { 0359 } 0360 0361 Q_SIGNALS: 0362 void signalSelected(int levelIndex); 0363 0364 public Q_SLOTS: 0365 void slotActivated(int levelIndex) 0366 { 0367 d->select(d->levels().value(levelIndex)); 0368 } 0369 void slotSelected(const KGameDifficultyLevel *level) 0370 { 0371 Q_EMIT signalSelected(d->levels().indexOf(level)); 0372 } 0373 }; 0374 0375 class Menu : public KSelectAction 0376 { 0377 Q_OBJECT 0378 0379 public: 0380 Menu(const QIcon &i, const QString &s, QWidget *p) 0381 : KSelectAction(i, s, p) 0382 { 0383 } 0384 0385 public Q_SLOTS: 0386 // this whole class just because the following is not a slot 0387 void setCurrentItem(int index) 0388 { 0389 KSelectAction::setCurrentItem(index); 0390 } 0391 }; 0392 } 0393 0394 void KGameDifficultyGUI::init(KXmlGuiWindow *window, KGameDifficulty *difficulty) 0395 { 0396 const bool useSingleton = !difficulty; 0397 if (useSingleton) 0398 difficulty = KGameDifficulty::global(); 0399 0400 // create selector (resides in status bar) 0401 KGameDifficultyGUI::Selector *selector = new KGameDifficultyGUI::Selector(difficulty, window); 0402 selector->setToolTip(i18nc("Game difficulty level", "Difficulty")); 0403 QObject::connect(selector, &QComboBox::activated, selector, &Selector::slotActivated); 0404 QObject::connect(difficulty, &KGameDifficulty::editableChanged, selector, &QWidget::setEnabled); 0405 QObject::connect(difficulty, &KGameDifficulty::selectedLevelChanged, selector, &Selector::slotSelected); 0406 QObject::connect(selector, &Selector::signalSelected, selector, &QComboBox::setCurrentIndex); 0407 0408 // create menu action 0409 const QIcon icon = QIcon::fromTheme(QStringLiteral("games-difficult")); 0410 KSelectAction *menu = new KGameDifficultyGUI::Menu(icon, i18nc("Game difficulty level", "Difficulty"), window); 0411 menu->setToolTip(i18n("Set the difficulty level")); 0412 menu->setWhatsThis(i18n("Set the difficulty level of the game.")); 0413 QObject::connect(menu, &KSelectAction::indexTriggered, selector, &Selector::slotActivated); 0414 QObject::connect(difficulty, &KGameDifficulty::editableChanged, menu, &QAction::setEnabled); 0415 QObject::connect(selector, &Selector::signalSelected, menu, &KSelectAction::setCurrentItem); 0416 0417 // fill menu and selector 0418 const auto levels = difficulty->levels(); 0419 for (const KGameDifficultyLevel *level : levels) { 0420 selector->addItem(icon, level->title()); 0421 menu->addAction(level->title()); 0422 } 0423 // initialize selection in selector 0424 selector->slotSelected(difficulty->currentLevel()); 0425 0426 // add selector to statusbar 0427 window->statusBar()->addPermanentWidget(selector); 0428 // add menu action to window 0429 menu->setObjectName(QStringLiteral("options_game_difficulty")); 0430 window->actionCollection()->addAction(menu->objectName(), menu); 0431 0432 // ensure that the KGameDifficulty instance gets deleted 0433 if (!useSingleton && !difficulty->parent()) { 0434 difficulty->setParent(window); 0435 } 0436 } 0437 0438 // END KGameDifficultyGUI 0439 0440 #include "kgamedifficulty.moc" 0441 #include "moc_kgamedifficulty.cpp"