KDEGames

kgamedifficulty.cpp
1/*
2 SPDX-FileCopyrightText: 2007 Nicolas Roffet <nicolas-kde@roffet.com>
3 SPDX-FileCopyrightText: 2007 Pino Toscano <toscano.pino@tiscali.it>
4 SPDX-FileCopyrightText: 2011-2012 Stefan Majewsky <majewsky@gmx.net>
5
6 SPDX-License-Identifier: LGPL-2.0-only
7*/
8
9#include "kgamedifficulty.h"
10
11// KF
12#include <KActionCollection>
13#include <KComboBox>
14#include <KConfigGroup>
15#include <KGuiItem>
16#include <KLocalizedString>
17#include <KMessageBox>
18#include <KSelectAction>
19#include <KSharedConfig>
20#include <KXmlGuiWindow>
21// Qt
22#include <QCoreApplication>
23#include <QIcon>
24#include <QList>
25#include <QStatusBar>
26// Std
27#include <algorithm>
28#include <utility>
29
30// BEGIN KGameDifficultyLevel
31
32class KGameDifficultyLevelPrivate
33{
34public:
35 bool m_isDefault;
36 int m_hardness;
38 QByteArray m_key;
39 QString m_title;
40
41public:
42 KGameDifficultyLevelPrivate(int hardness, const QByteArray &key, const QString &title, KGameDifficultyLevel::StandardLevel level, bool isDefault);
43 static KGameDifficultyLevelPrivate *fromStandardLevel(KGameDifficultyLevel::StandardLevel level, bool isDefault);
44};
45
46KGameDifficultyLevel::KGameDifficultyLevel(int hardness, const QByteArray &key, const QString &title, bool isDefault)
47 : d_ptr(new KGameDifficultyLevelPrivate(hardness, key, title, Custom, isDefault))
48{
49}
50
51KGameDifficultyLevelPrivate::KGameDifficultyLevelPrivate(int hardness,
52 const QByteArray &key,
53 const QString &title,
55 bool isDefault)
56 : m_isDefault(isDefault)
57 , m_hardness(hardness)
58 , m_level(level)
59 , m_key(key)
60 , m_title(title)
61{
62}
63
64KGameDifficultyLevel::KGameDifficultyLevel(StandardLevel level, bool isDefault)
65 : d_ptr(KGameDifficultyLevelPrivate::fromStandardLevel(level, isDefault))
66{
67}
68
69KGameDifficultyLevelPrivate *KGameDifficultyLevelPrivate::fromStandardLevel(KGameDifficultyLevel::StandardLevel level, bool isDefault)
70{
71 Q_ASSERT_X(level != KGameDifficultyLevel::Custom, "KGameDifficultyLevel(StandardLevel) constructor", "Custom level not allowed here");
72 // 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
73 QPair<QByteArray, QString> data;
74 switch (level) {
75 case KGameDifficultyLevel::RidiculouslyEasy:
76 data = qMakePair(QByteArrayLiteral("Ridiculously Easy"), i18nc("Game difficulty level 1 out of 8", "Ridiculously Easy"));
77 break;
78 case KGameDifficultyLevel::VeryEasy:
79 data = qMakePair(QByteArrayLiteral("Very Easy"), i18nc("Game difficulty level 2 out of 8", "Very Easy"));
80 break;
81 case KGameDifficultyLevel::Easy:
82 data = qMakePair(QByteArrayLiteral("Easy"), i18nc("Game difficulty level 3 out of 8", "Easy"));
83 break;
84 case KGameDifficultyLevel::Medium:
85 data = qMakePair(QByteArrayLiteral("Medium"), i18nc("Game difficulty level 4 out of 8", "Medium"));
86 break;
87 case KGameDifficultyLevel::Hard:
88 data = qMakePair(QByteArrayLiteral("Hard"), i18nc("Game difficulty level 5 out of 8", "Hard"));
89 break;
90 case KGameDifficultyLevel::VeryHard:
91 data = qMakePair(QByteArrayLiteral("Very Hard"), i18nc("Game difficulty level 6 out of 8", "Very Hard"));
92 break;
93 case KGameDifficultyLevel::ExtremelyHard:
94 data = qMakePair(QByteArrayLiteral("Extremely Hard"), i18nc("Game difficulty level 7 out of 8", "Extremely Hard"));
95 break;
96 case KGameDifficultyLevel::Impossible:
97 data = qMakePair(QByteArrayLiteral("Impossible"), i18nc("Game difficulty level 8 out of 8", "Impossible"));
98 break;
100 return nullptr;
101 }
102 return new KGameDifficultyLevelPrivate(level, data.first, data.second, level, isDefault);
103}
104
105KGameDifficultyLevel::~KGameDifficultyLevel() = default;
106
108{
110
111 return d->m_isDefault;
112}
113
114int KGameDifficultyLevel::hardness() const
115{
117
118 return d->m_hardness;
119}
120
121QByteArray KGameDifficultyLevel::key() const
122{
124
125 return d->m_key;
126}
127
128QString KGameDifficultyLevel::title() const
129{
131
132 return d->m_title;
133}
134
135KGameDifficultyLevel::StandardLevel KGameDifficultyLevel::standardLevel() const
136{
138
139 return d->m_level;
140}
141
142// END KGameDifficultyLevel
143// BEGIN KGameDifficulty
144
145class KGameDifficultyPrivate
146{
147public:
149 mutable const KGameDifficultyLevel *m_currentLevel = nullptr;
150 bool m_editable = true;
151 bool m_gameRunning = false;
152
153public:
154 KGameDifficultyPrivate() = default;
155};
156
157static void saveLevel()
158{
159 // save current difficulty level in config file (no sync() call here; this
160 // will most likely be called at application shutdown when others are also
161 // writing to KGlobal::config(); also KConfig's dtor will sync automatically)
162 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KgDifficulty"));
163 cg.writeEntry("Level", KGameDifficulty::global()->currentLevel()->key());
164}
165
166KGameDifficulty::KGameDifficulty(QObject *parent)
167 : QObject(parent)
168 , d_ptr(new KGameDifficultyPrivate)
169{
170 qRegisterMetaType<const KGameDifficultyLevel *>();
171 qAddPostRoutine(saveLevel);
172}
173
175{
177
178 qDeleteAll(d->m_levels);
179}
180
182{
184
185 // The intended use is to create the KGameDifficulty object, add levels, *then*
186 // start to work with the currentLevel(). The first call to currentLevel()
187 // will load the previous selection from the config, and the level list will
188 // be considered immutable from this point.
189 Q_ASSERT_X(d->m_currentLevel == nullptr, "KGameDifficulty::addLevel", "Only allowed before currentLevel() is called.");
190 // ensure that list stays sorted
191 const int newLevelHardness = level->hardness();
192 auto it = std::find_if(d->m_levels.begin(), d->m_levels.end(), [newLevelHardness](const KGameDifficultyLevel *l) {
193 return l->hardness() >= newLevelHardness;
194 });
195 d->m_levels.insert(it, level);
196 level->setParent(this);
197}
198
200
201void KGameDifficulty::addStandardLevel(DS level, bool isDefault)
202{
203 addLevel(new KGameDifficultyLevel(level, isDefault));
204}
205
207{
208 // every level in range != Custom, therefore no level is default
210}
211
212void KGameDifficulty::addStandardLevelRange(DS from, DS to, DS defaultLevel)
213{
214 const QList<DS> levels{
215 KGameDifficultyLevel::RidiculouslyEasy,
216 KGameDifficultyLevel::VeryEasy,
217 KGameDifficultyLevel::Easy,
218 KGameDifficultyLevel::Medium,
219 KGameDifficultyLevel::Hard,
220 KGameDifficultyLevel::VeryHard,
221 KGameDifficultyLevel::ExtremelyHard,
222 KGameDifficultyLevel::Impossible,
223 };
224 const int fromIndex = levels.indexOf(from);
225 const int toIndex = levels.indexOf(to);
226 const int defaultLevelIndex = levels.indexOf(defaultLevel);
227 Q_ASSERT_X(fromIndex >= 0 && toIndex > fromIndex
228 && (defaultLevelIndex == KGameDifficultyLevel::Custom || (defaultLevelIndex >= fromIndex && defaultLevelIndex <= toIndex)),
229 "KGameDifficulty::addStandardLevelRange",
230 "No argument may be KGameDifficultyLevel::Custom.");
231 for (int i = fromIndex; i <= toIndex; ++i) {
232 addLevel(new KGameDifficultyLevel(levels[i], levels[i] == defaultLevel));
233 }
234}
235
237{
238 Q_D(const KGameDifficulty);
239
240 return d->m_levels;
241}
242
243const KGameDifficultyLevel *KGameDifficulty::currentLevel() const
244{
245 Q_D(const KGameDifficulty);
246
247 if (d->m_currentLevel) {
248 return d->m_currentLevel;
249 }
250 Q_ASSERT(!d->m_levels.isEmpty());
251 // check configuration file for saved difficulty level
252 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KgDifficulty"));
253 const QByteArray key = cg.readEntry("Level", QByteArray());
254 for (const KGameDifficultyLevel *level : std::as_const(d->m_levels)) {
255 if (level->key() == key) {
256 return d->m_currentLevel = level;
257 }
258 }
259 // no level predefined - look for a default level
260 for (const KGameDifficultyLevel *level : std::as_const(d->m_levels)) {
261 if (level->isDefault()) {
262 return d->m_currentLevel = level;
263 }
264 }
265 // no default level predefined - easiest level is probably a sane default
266 return d->m_currentLevel = d->m_levels[0];
267}
268
270{
271 Q_D(const KGameDifficulty);
272
273 return d->m_editable;
274}
275
277{
279
280 if (d->m_editable == editable) {
281 return;
282 }
283 d->m_editable = editable;
284 Q_EMIT editableChanged(editable);
285}
286
288{
289 Q_D(const KGameDifficulty);
290
291 return d->m_gameRunning;
292}
293
295{
297
298 if (d->m_gameRunning == gameRunning) {
299 return;
300 }
301 d->m_gameRunning = gameRunning;
302 Q_EMIT gameRunningChanged(gameRunning);
303}
304
306{
308
309 Q_ASSERT(d->m_levels.contains(level));
310 if (d->m_currentLevel == level) {
311 return;
312 }
313 // ask for confirmation if necessary
314 if (d->m_gameRunning) {
315 const int result = KMessageBox::warningContinueCancel(nullptr,
316 i18n("Changing the difficulty level will end the current game!"),
317 QString(),
318 KGuiItem(i18nc("@action:button", "Change the Difficulty Level")));
319 if (result != KMessageBox::Continue) {
320 Q_EMIT selectedLevelChanged(d->m_currentLevel);
321 return;
322 }
323 }
324 d->m_currentLevel = level;
327}
328
329// END KGameDifficulty
330
331Q_GLOBAL_STATIC(KGameDifficulty, g_difficulty)
332
334{
335 return g_difficulty;
336}
337
339{
340 return g_difficulty->currentLevel()->standardLevel();
341}
342
343// BEGIN KGameDifficultyGUI
344
346{
347class Selector : public KComboBox
348{
350
351private:
353
354public:
355 Selector(KGameDifficulty *difficulty, QWidget *parent = nullptr)
357 , d(difficulty)
358 {
359 }
360
362 void signalSelected(int levelIndex);
363
364public Q_SLOTS:
365 void slotActivated(int levelIndex)
366 {
367 d->select(d->levels().value(levelIndex));
368 }
369 void slotSelected(const KGameDifficultyLevel *level)
370 {
371 Q_EMIT signalSelected(d->levels().indexOf(level));
372 }
373};
374
375class Menu : public KSelectAction
376{
378
379public:
380 Menu(const QIcon &i, const QString &s, QWidget *p)
381 : KSelectAction(i, s, p)
382 {
383 }
384
385public Q_SLOTS:
386 // this whole class just because the following is not a slot
387 void setCurrentItem(int index)
388 {
390 }
391};
392}
393
395{
396 const bool useSingleton = !difficulty;
397 if (useSingleton)
398 difficulty = KGameDifficulty::global();
399
400 // create selector (resides in status bar)
401 KGameDifficultyGUI::Selector *selector = new KGameDifficultyGUI::Selector(difficulty, window);
402 selector->setToolTip(i18nc("@info:tooltip Game difficulty level", "Difficulty"));
403 QObject::connect(selector, &QComboBox::activated, selector, &Selector::slotActivated);
405 QObject::connect(difficulty, &KGameDifficulty::selectedLevelChanged, selector, &Selector::slotSelected);
406 QObject::connect(selector, &Selector::signalSelected, selector, &QComboBox::setCurrentIndex);
407
408 // create menu action
409 const QIcon icon = QIcon::fromTheme(QStringLiteral("games-difficult"));
410 KSelectAction *menu = new KGameDifficultyGUI::Menu(icon, i18nc("@title:menu Game difficulty level", "Difficulty"), window);
411 menu->setToolTip(i18nc("@info:tooltip", "Set the difficulty level"));
412 menu->setWhatsThis(i18nc("@info:whatsthis", "Sets the difficulty level of the game."));
413 QObject::connect(menu, &KSelectAction::indexTriggered, selector, &Selector::slotActivated);
415 QObject::connect(selector, &Selector::signalSelected, menu, &KSelectAction::setCurrentItem);
416
417 // fill menu and selector
418 const auto levels = difficulty->levels();
419 for (const KGameDifficultyLevel *level : levels) {
420 selector->addItem(icon, level->title());
421 menu->addAction(level->title());
422 }
423 // initialize selection in selector
424 selector->slotSelected(difficulty->currentLevel());
425
426 // add selector to statusbar
427 window->statusBar()->addPermanentWidget(selector);
428 // add menu action to window
429 menu->setObjectName(QStringLiteral("options_game_difficulty"));
430 window->actionCollection()->addAction(menu->objectName(), menu);
431
432 // ensure that the KGameDifficulty instance gets deleted
433 if (!useSingleton && !difficulty->parent()) {
434 difficulty->setParent(window);
435 }
436}
437
438// END KGameDifficultyGUI
439
440#include "kgamedifficulty.moc"
441#include "moc_kgamedifficulty.cpp"
KComboBox(bool rw, QWidget *parent=nullptr)
QString readEntry(const char *key, const char *aDefault=nullptr) const
@ Custom
standardLevel() returns this for custom levels.
KGameDifficultyLevel(int hardness, const QByteArray &key, const QString &title, bool isDefault=false)
Refer to the getters' documentation for details on the params.
KGameDifficulty manages difficulty levels of a game in a standard way.
static KGameDifficulty * global()
void selectedLevelChanged(const KGameDifficultyLevel *level)
Emitted after every call to select(), even when the user has rejected the change.
void editableChanged(bool editable)
Emitted when the editability changes.
static KGameDifficultyLevel::StandardLevel globalLevel()
A shortcut for KGameDifficulty::global()->currentLevel()->standardLevel().
void gameRunningChanged(bool gameRunning)
Emitted when a running game has been marked or unmarked.
void addStandardLevel(KGameDifficultyLevel::StandardLevel level, bool isDefault=false)
A shortcut for addLevel(new KGameDifficultyLevel(level)).
void select(const KGameDifficultyLevel *level)
Select a new difficulty level.
void setEditable(bool editable)
Set whether the difficulty level selection may be edited.
void setGameRunning(bool running)
KGameDifficulty has optional protection against changing the difficulty level while a game is running...
bool isGameRunning() const
void addLevel(KGameDifficultyLevel *level)
Adds a difficulty level to this instance.
bool isEditable() const
~KGameDifficulty() override
Destroys this instance and all DifficultyLevel instances in it.
void addStandardLevelRange(KGameDifficultyLevel::StandardLevel from, KGameDifficultyLevel::StandardLevel to)
This convenience method adds a range of standard levels to this instance (including the boundaries).
QList< const KGameDifficultyLevel * > levels() const
void currentLevelChanged(const KGameDifficultyLevel *level)
Emitted when a new difficulty level has been selected.
QAction * addAction(const QIcon &icon, const QString &text)
KSelectAction(const QIcon &icon, const QString &text, QObject *parent)
void indexTriggered(int index)
bool setCurrentItem(int index)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
The namespace for methods to integrate KGameDifficulty into the UI.
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
Install standard GUI components for the manipulation of the given KGameDifficulty instance in the giv...
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
QStringView level(QStringView ifopt)
void setEnabled(bool)
void setToolTip(const QString &tip)
void setWhatsThis(const QString &what)
void activated(int index)
void addItem(const QIcon &icon, const QString &text, const QVariant &userData)
void setCurrentIndex(int index)
QIcon fromTheme(const QString &name)
qsizetype indexOf(const AT &value, qsizetype from) const const
T value(qsizetype i) const const
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
void setObjectName(QAnyStringView name)
void setParent(QObject *parent)
QAction * addAction(const QIcon &icon, const QString &text)
void setEnabled(bool)
void setToolTip(const QString &)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:06:43 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.