KDEGames

kgamedifficulty.cpp
1 /*
2  SPDX-FileCopyrightText: 2007 Nicolas Roffet <[email protected]>
3  SPDX-FileCopyrightText: 2007 Pino Toscano <[email protected]>
4  SPDX-FileCopyrightText: 2011-2012 Stefan Majewsky <[email protected]>
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 
32 class KGameDifficultyLevelPrivate
33 {
34 public:
35  bool m_isDefault;
36  int m_hardness;
38  QByteArray m_key;
39  QString m_title;
40 
41 public:
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 
46 KGameDifficultyLevel::KGameDifficultyLevel(int hardness, const QByteArray &key, const QString &title, bool isDefault)
47  : d_ptr(new KGameDifficultyLevelPrivate(hardness, key, title, Custom, isDefault))
48 {
49 }
50 
51 KGameDifficultyLevelPrivate::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 
64 KGameDifficultyLevel::KGameDifficultyLevel(StandardLevel level, bool isDefault)
65  : d_ptr(KGameDifficultyLevelPrivate::fromStandardLevel(level, isDefault))
66 {
67 }
68 
69 KGameDifficultyLevelPrivate *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
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 
105 KGameDifficultyLevel::~KGameDifficultyLevel() = default;
106 
108 {
109  Q_D(const KGameDifficultyLevel);
110 
111  return d->m_isDefault;
112 }
113 
114 int KGameDifficultyLevel::hardness() const
115 {
116  Q_D(const KGameDifficultyLevel);
117 
118  return d->m_hardness;
119 }
120 
121 QByteArray KGameDifficultyLevel::key() const
122 {
123  Q_D(const KGameDifficultyLevel);
124 
125  return d->m_key;
126 }
127 
128 QString KGameDifficultyLevel::title() const
129 {
130  Q_D(const KGameDifficultyLevel);
131 
132  return d->m_title;
133 }
134 
135 KGameDifficultyLevel::StandardLevel KGameDifficultyLevel::standardLevel() const
136 {
137  Q_D(const KGameDifficultyLevel);
138 
139  return d->m_level;
140 }
141 
142 // END KGameDifficultyLevel
143 // BEGIN KGameDifficulty
144 
145 class KGameDifficultyPrivate
146 {
147 public:
149  mutable const KGameDifficultyLevel *m_currentLevel = nullptr;
150  bool m_editable = true;
151  bool m_gameRunning = false;
152 
153 public:
154  KGameDifficultyPrivate() = default;
155 };
156 
157 static 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 
166 KGameDifficulty::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 
201 void 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 
212 void 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 
243 const 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 
276 void KGameDifficulty::setEditable(bool editable)
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 
294 void KGameDifficulty::setGameRunning(bool gameRunning)
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(i18n("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 
331 Q_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 {
347 class Selector : public KComboBox
348 {
349  Q_OBJECT
350 
351 private:
352  KGameDifficulty *d;
353 
354 public:
355  Selector(KGameDifficulty *difficulty, QWidget *parent = nullptr)
356  : KComboBox(parent)
357  , d(difficulty)
358  {
359  }
360 
361 Q_SIGNALS:
362  void signalSelected(int levelIndex);
363 
364 public 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 
375 class Menu : public KSelectAction
376 {
377  Q_OBJECT
378 
379 public:
380  Menu(const QIcon &i, const QString &s, QWidget *p)
381  : KSelectAction(i, s, p)
382  {
383  }
384 
385 public 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("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("Game difficulty level", "Difficulty"), window);
411  menu->setToolTip(i18n("Set the difficulty level"));
412  menu->setWhatsThis(i18n("Set 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"
void currentLevelChanged(const KGameDifficultyLevel *level)
Emitted when a new difficulty level has been selected.
void addStandardLevelRange(KGameDifficultyLevel::StandardLevel from, KGameDifficultyLevel::StandardLevel to)
This convenience method adds a range of standard levels to this instance (including the boundaries).
KGameDifficultyLevel(int hardness, const QByteArray &key, const QString &title, bool isDefault=false)
Refer to the getters' documentation for details on the params.
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)
Q_EMITQ_EMIT
void editableChanged(bool editable)
Emitted when the editability changes.
QIcon fromTheme(const QString &name)
QStringView level(QStringView ifopt)
void setCurrentIndex(int index)
QAction * addAction(const QIcon &icon, const QString &text)
KGameDifficulty manages difficulty levels of a game in a standard way.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
void indexTriggered(int index)
QList< const KGameDifficultyLevel * > levels() const
QString i18n(const char *text, const TYPE &arg...)
int indexOf(const T &value, int from) const const
static KGameDifficulty * global()
void addLevel(KGameDifficultyLevel *level)
Adds a difficulty level to this instance.
~KGameDifficulty() override
Destroys this instance and all DifficultyLevel instances in it.
void gameRunningChanged(bool gameRunning)
Emitted when a running game has been marked or unmarked.
void setWhatsThis(const QString &what)
void setEnabled(bool)
void select(const KGameDifficultyLevel *level)
Select a new difficulty level.
void addStandardLevel(KGameDifficultyLevel::StandardLevel level, bool isDefault=false)
A shortcut for addLevel(new KGameDifficultyLevel(level)).
bool isEditable() const
void setGameRunning(bool running)
KGameDifficulty has optional protection against changing the difficulty level while a game is running...
void setToolTip(const QString &tip)
void setEnabled(bool)
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
Install standard GUI components for the manipulation of the given KGameDifficulty instance in the giv...
void setObjectName(const QString &name)
void setParent(QObject *parent)
bool setCurrentItem(int index)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
static KGameDifficultyLevel::StandardLevel globalLevel()
A shortcut for KGameDifficulty::global()->currentLevel()->standardLevel().
bool isGameRunning() const
void addAction(QAction *action)
void setEditable(bool editable)
Set whether the difficulty level selection may be edited.
void selectedLevelChanged(const KGameDifficultyLevel *level)
Emitted after every call to select(), even when the user has rejected the change.
@ Custom
standardLevel() returns this for custom levels.
void activated(int index)
QObject * parent() const const
T value(int i) const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Dec 7 2023 03:50:24 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.