File indexing completed on 2024-10-06 03:45:28
0001 /* 0002 SPDX-FileCopyrightText: 2002 Marco Krüger <grisuji@gmx.de> 0003 SPDX-FileCopyrightText: 2002 Ian Wadham <iandw.au@gmail.com> 0004 SPDX-FileCopyrightText: 2009 Ian Wadham <iandw.au@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "kgoldrunner.h" 0010 0011 #include <QAction> 0012 #include <QActionGroup> 0013 #include <QIcon> 0014 #include <QKeyEvent> 0015 #include <QKeySequence> 0016 #include <QScreen> 0017 #include <QShortcut> 0018 0019 #include <KActionCollection> 0020 #include <KConfig> 0021 #include <KConfigGroup> 0022 #include <KIO/MkpathJob> 0023 #include <KLocalizedString> 0024 #include <KSharedConfig> 0025 #include <KShortcutsDialog> 0026 #include <KStandardAction> 0027 #include <KGameStandardAction> 0028 #include <KToggleAction> 0029 #include <KToggleFullScreenAction> 0030 #include <KToolBar> 0031 0032 #include "kgoldrunner_debug.h" 0033 0034 0035 #include <libkdegames_capabilities.h> //defines KGAUDIO_BACKEND_OPENAL (or not) 0036 0037 #include "kgrgame.h" 0038 #include "kgrview.h" 0039 #include "kgrscene.h" 0040 #include "kgrrenderer.h" 0041 0042 // Shorthand for references to actions. 0043 #define ACTION(x) (actionCollection()->action(x)) 0044 0045 KGoldrunner::KGoldrunner() 0046 : 0047 KXmlGuiWindow (nullptr) 0048 { 0049 /******************************************************************************/ 0050 /************* FIND WHERE THE GAMES DATA AND HANDBOOK SHOULD BE *************/ 0051 /******************************************************************************/ 0052 0053 setObjectName ( QStringLiteral("KGoldrunner" )); 0054 0055 // Avoid "saveOK()" check if an error-exit occurs during the file checks. 0056 startupOK = true; 0057 0058 // Get directory paths for the system levels, user levels and manual. 0059 if (! getDirectories()) { 0060 fprintf (stderr, "getDirectories() FAILED\n"); 0061 startupOK = false; 0062 return; // If games directory not found, abort. 0063 } 0064 0065 // This message is to help diagnose distribution or installation problems. 0066 qCDebug(KGOLDRUNNER_LOG, "The games data should be in the following locations:\n" 0067 "System games: %s\nUser data: %s", 0068 qPrintable(systemDataDir), qPrintable(userDataDir)); 0069 0070 /******************************************************************************/ 0071 /************************ SET PLAYFIELD AND GAME DATA ***********************/ 0072 /******************************************************************************/ 0073 0074 const QSize maxSize = screen()->availableGeometry().size(); 0075 // Base the size of playing-area and widgets on the monitor resolution. 0076 int dw = maxSize.width(); 0077 0078 // Need to consider the height, for widescreen displays (eg. 1280x768). 0079 int dh = maxSize.height(); 0080 0081 dw = qMin ((4 * dh + 1) / 3, dw); // KGoldrunner aspect ratio is 4:3. 0082 dh = (3 * dw + 2) / 4; 0083 0084 view = new KGrView (this); 0085 view->setMinimumSize ((dw + 1) / 2, (dh + 1) / 2); 0086 0087 game = new KGrGame (view, systemDataDir, userDataDir); 0088 0089 // Initialise the lists of games (i.e. collections of levels). 0090 if (! game->initGameLists()) { 0091 startupOK = false; 0092 return; // If no game files, abort. 0093 } 0094 0095 /******************************************************************************/ 0096 /************************* SET UP THE USER INTERFACE ************************/ 0097 /******************************************************************************/ 0098 0099 // Tell the KMainWindow that the KGrView object is the main widget. 0100 setCentralWidget (view); 0101 0102 scene = view->gameScene (); 0103 renderer = scene->renderer (); 0104 0105 // Set up our actions (menu, toolbar and keystrokes) ... 0106 setupActions(); 0107 0108 // Do NOT put show/hide actions for the statusbar and toolbar in the GUI. 0109 // We do not have a statusbar any more and the toolbar is relevant only 0110 // when using the game editor and then it appears automatically. Maybe 1% 0111 // of players would use the game editor for 5% of their time. Also we have 0112 // our own action to configure shortcut keys, so disable the KXmlGui one. 0113 setupGUI (static_cast<StandardWindowOption> (Default & 0114 (~StatusBar) & (~ToolBar) & (~Keys))); 0115 0116 // Initialize text-item lengths in the scene, before the first resize. 0117 scene->showLives (0); 0118 scene->showScore (0); 0119 adjustHintAction (false); 0120 gameFreeze (false); 0121 0122 // Connect the game actions to the menu and toolbar displays. 0123 connect (game, &KGrGame::quitGame, this, &KGoldrunner::close); 0124 connect (game, &KGrGame::setEditMenu, this, &KGoldrunner::setEditMenu); 0125 connect (game, &KGrGame::showLives, scene, &KGrScene::showLives); 0126 connect (game, &KGrGame::showScore, scene, &KGrScene::showScore); 0127 connect (game, &KGrGame::hintAvailable, this, &KGoldrunner::adjustHintAction); 0128 connect (game, &KGrGame::gameFreeze, this, &KGoldrunner::gameFreeze); 0129 0130 connect (game, &KGrGame::setAvail, this, &KGoldrunner::setAvail); 0131 connect (game, &KGrGame::setToggle, this, &KGoldrunner::setToggle); 0132 0133 connect (scene, &KGrScene::redrawEditToolbar, this, &KGoldrunner::redrawEditToolbar); 0134 0135 // Apply the saved mainwindow settings, if any, and ask the mainwindow 0136 // to automatically save settings if changed: window size, toolbar 0137 // position, icon size, etc. 0138 setAutoSaveSettings(); 0139 0140 // Explicitly hide the edit toolbar - we need it in edit mode only and we 0141 // always start in play mode, even if the last session ended in edit mode. 0142 // Besides, we cannot render it until after the initial resize event (s). 0143 toolBar (QStringLiteral("editToolbar"))->hide(); 0144 0145 // This is needed to make the arrow keys control the hero properly. 0146 setUpKeyboardControl(); 0147 0148 // Do NOT paint main widget yet (title bar, menu, blank playfield). 0149 // Instead, queue a call to the "KGoldrunner_2" constructor extension. 0150 QMetaObject::invokeMethod (this, "KGoldrunner_2", Qt::QueuedConnection); 0151 //qCDebug(KGOLDRUNNER_LOG) << "QMetaObject::invokeMethod (this, \"KGoldrunner_2\") done ... "; 0152 //qCDebug(KGOLDRUNNER_LOG) << "1st scan of event-queue ..."; 0153 } 0154 0155 void KGoldrunner::KGoldrunner_2() 0156 { 0157 //qCDebug(KGOLDRUNNER_LOG) << "Entered constructor extension ..."; 0158 0159 // Queue a call to the "initGame" method. This renders and paints the 0160 // initial graphics, but only AFTER the initial main-window resize events 0161 // have been seen and the final SVG scale is known. 0162 QMetaObject::invokeMethod (game, "initGame", Qt::QueuedConnection); 0163 //qCDebug(KGOLDRUNNER_LOG) << "QMetaObject::invokeMethod (game, \"initGame\") done ... "; 0164 //qCDebug(KGOLDRUNNER_LOG) << "2nd scan of event-queue ..."; 0165 } 0166 0167 KGoldrunner::~KGoldrunner() 0168 { 0169 } 0170 0171 void KGoldrunner::setupActions() 0172 { 0173 /**************************************************************************/ 0174 /****************************** GAME MENU ******************************/ 0175 /**************************************************************************/ 0176 0177 // New Game... 0178 // Load Saved Game... 0179 // -------------------------- 0180 0181 QAction * a = KGameStandardAction::gameNew (this, nullptr, nullptr); 0182 actionCollection()->addAction (a->objectName(), a); 0183 connect (a, &QAction::triggered, this, [this] { game->gameActions(NEW); }); 0184 a->setText (i18n ("&New Game...")); 0185 0186 a = gameAction (QStringLiteral("next_level"), NEXT_LEVEL, 0187 i18n ("Pla&y Next Level"), 0188 i18n ("Play next level."), 0189 i18n ("Try the next level in the game " 0190 "you are playing."), 0191 Qt::Key_Y); 0192 0193 a = KGameStandardAction::load (this, nullptr, nullptr); 0194 actionCollection()->addAction (a->objectName(), a); 0195 connect (a, &QAction::triggered, this, [this] { game->gameActions(LOAD); }); 0196 a->setText (i18n ("&Load Saved Game...")); 0197 0198 // Save Game... 0199 // Save Solution... 0200 // -------------------------- 0201 0202 saveGame = KGameStandardAction::save (this, nullptr, nullptr); 0203 actionCollection()->addAction (saveGame->objectName(), saveGame); 0204 connect (saveGame, &QAction::triggered, this, [this] { game->gameActions(SAVE_GAME); }); 0205 saveGame->setText (i18n ("&Save Game...")); 0206 KActionCollection::setDefaultShortcut(saveGame, Qt::Key_S); // Alternate key. 0207 0208 // The name of the solution-file is 'sol_<prefix>.txt', where <prefix> is 0209 // the unique prefix belonging to the game involved (eg. plws, tute, etc.). 0210 a = gameAction (QStringLiteral("save_solution"), SAVE_SOLUTION, 0211 i18n ("Save A Solution..."), 0212 i18n ("Save A Solution..."), 0213 i18n ("Save a solution for a level into a file " 0214 "called 'sol_<prefix>.txt' in your " 0215 "user's data directory..."), 0216 {Qt::ShiftModifier | Qt::Key_S}); 0217 0218 // Pause 0219 // Show High Scores 0220 // Get a Hint 0221 // Kill the Hero 0222 // -------------------------- 0223 0224 myPause = KGameStandardAction::pause (this, nullptr, nullptr); 0225 actionCollection()->addAction (myPause->objectName(), myPause); 0226 connect (myPause, &QAction::triggered, this, [this] { game->gameActions(PAUSE); }); 0227 // QAction * myPause gets QAction::shortcut(), returning 1 OR 2 shortcuts. 0228 QList<QKeySequence> pauseShortcut = { myPause->shortcut(), Qt::Key_Escape }; 0229 myPause->setShortcuts (pauseShortcut); 0230 0231 highScore = KGameStandardAction::highscores (this, nullptr, nullptr); 0232 actionCollection()->addAction (highScore->objectName(), highScore); 0233 connect (highScore, &QAction::triggered, this, [this] { game->gameActions(HIGH_SCORE); }); 0234 0235 hintAction = KGameStandardAction::hint (this, nullptr, nullptr); 0236 actionCollection()->addAction (hintAction->objectName(), hintAction); 0237 connect (hintAction, &QAction::triggered, this, [this] { game->gameActions(HINT); }); 0238 0239 a = KGameStandardAction::demo (this, nullptr, nullptr); 0240 actionCollection()->addAction (a->objectName(), a); 0241 connect (a, &QAction::triggered, this, [this] { game->gameActions(DEMO); }); 0242 0243 a = KGameStandardAction::solve (this, nullptr, nullptr); 0244 actionCollection()->addAction (a->objectName(), a); 0245 connect (a, &QAction::triggered, this, [this] { game->gameActions(SOLVE); }); 0246 a->setText (i18n ("&Show a Solution")); 0247 a->setToolTip (i18n ("Show how to win this level.")); 0248 a->setWhatsThis (i18n ("Play a recording of how to win this level, if " 0249 "there is one available.")); 0250 0251 a = gameAction (QStringLiteral("instant_replay"), INSTANT_REPLAY, 0252 i18n ("&Instant Replay"), 0253 i18n ("Instant replay."), 0254 i18n ("Show a recording of the level " 0255 "you are currently playing."), 0256 Qt::Key_R); 0257 0258 a = gameAction (QStringLiteral("replay_last"), REPLAY_LAST, 0259 i18n ("Replay &Last Level"), 0260 i18n ("Replay last level."), 0261 i18n ("Show a recording of the last level you " 0262 "played and finished, regardless of whether " 0263 "you won or lost."), 0264 Qt::Key_A); 0265 0266 a = gameAction (QStringLiteral("replay_any"), REPLAY_ANY, 0267 i18n ("&Replay Any Level"), 0268 i18n ("Replay any level."), 0269 i18n ("Show a recording of any level you have " 0270 "played so far."), 0271 QKeySequence()); // No key assigned. 0272 0273 killHero = gameAction (QStringLiteral("kill_hero"), KILL_HERO, 0274 i18n ("&Kill Hero"), 0275 i18n ("Kill Hero."), 0276 i18n ("Kill the hero, in case he finds himself in " 0277 "a situation from which he cannot escape."), 0278 Qt::Key_Q); 0279 0280 // Quit 0281 // -------------------------- 0282 0283 KGameStandardAction::quit (this, &QWidget::close, actionCollection()); 0284 0285 /**************************************************************************/ 0286 /*************************** GAME EDITOR MENU **************************/ 0287 /**************************************************************************/ 0288 // Create a Level 0289 // Edit a Level... 0290 // -------------------------- 0291 0292 QAction * ed = editAction (QStringLiteral("create_level"), CREATE_LEVEL, 0293 i18n ("&Create Level"), 0294 i18n ("Create level."), 0295 i18n ("Create a completely new level.")); 0296 ed->setIcon (QIcon::fromTheme( QStringLiteral( "document-new" ))); 0297 ed->setIconText (i18n ("Create")); 0298 0299 ed = editAction (QStringLiteral("edit_any"), EDIT_ANY, 0300 i18n ("&Edit Level..."), 0301 i18n ("Edit level..."), 0302 i18n ("Edit any level...")); 0303 ed->setIcon (QIcon::fromTheme( QStringLiteral( "document-open" ))); 0304 ed->setIconText (i18n ("Edit")); 0305 0306 // Save Edits... 0307 // Move Level... 0308 // Delete Level... 0309 // -------------------------- 0310 0311 saveEdits = editAction (QStringLiteral("save_edits"), SAVE_EDITS, 0312 i18n ("&Save Edits..."), 0313 i18n ("Save edits..."), 0314 i18n ("Save your level after editing...")); 0315 saveEdits->setIcon (QIcon::fromTheme( QStringLiteral( "document-save" ))); 0316 saveEdits->setIconText (i18n ("Save")); 0317 saveEdits->setEnabled (false); // Nothing to save, yet. 0318 0319 ed = editAction (QStringLiteral("move_level"), MOVE_LEVEL, 0320 i18n ("&Move Level..."), 0321 i18n ("Move level..."), 0322 i18n ("Change a level's number or move " 0323 "it to another game...")); 0324 0325 ed = editAction (QStringLiteral("delete_level"), DELETE_LEVEL, 0326 i18n ("&Delete Level..."), 0327 i18n ("Delete level..."), 0328 i18n ("Delete a level...")); 0329 0330 // Create a Game 0331 // Edit Game Info... 0332 // -------------------------- 0333 0334 ed = editAction (QStringLiteral("create_game"), CREATE_GAME, 0335 i18n ("Create &Game..."), 0336 i18n ("Create game..."), 0337 i18n ("Create a completely new game...")); 0338 0339 ed = editAction (QStringLiteral("edit_game"), EDIT_GAME, 0340 i18n ("Edit Game &Info..."), 0341 i18n ("Edit game info..."), 0342 i18n ("Change the name, rules or description " 0343 "of a game...")); 0344 0345 /**************************************************************************/ 0346 /**************************** SETTINGS MENU ****************************/ 0347 /**************************************************************************/ 0348 0349 // Theme settings are handled by this class and KGrRenderer. 0350 QAction * themes = actionCollection()->addAction (QStringLiteral("select_theme")); 0351 themes->setText (i18n ("Change &Theme...")); 0352 themes->setToolTip (i18n ("Change the graphics theme...")); 0353 themes->setWhatsThis (i18n ("Alter the visual appearance of the runners " 0354 "and background scene...")); 0355 connect (themes, &QAction::triggered, this, &KGoldrunner::changeTheme); 0356 0357 // Show/Exit Full Screen Mode 0358 KToggleFullScreenAction * fullScreen = KStandardAction::fullScreen 0359 (this, &KGoldrunner::viewFullScreen, this, this); 0360 actionCollection()->addAction (fullScreen->objectName(), fullScreen); 0361 0362 // Other settings are handled by KGrGame. 0363 0364 #ifdef KGAUDIO_BACKEND_OPENAL 0365 // Sound effects on/off 0366 settingAction (QStringLiteral("options_sounds"), PLAY_SOUNDS, 0367 i18n ("&Play Sounds"), 0368 i18n ("Play sound effects."), 0369 i18n ("Play sound effects during the game.")); 0370 0371 settingAction (QStringLiteral("options_steps"), PLAY_STEPS, 0372 i18n ("Play &Footstep Sounds"), 0373 i18n ("Make sounds of player's footsteps."), 0374 i18n ("Make sounds of player's footsteps.")); 0375 #endif 0376 0377 // Demo at start on/off. 0378 settingAction (QStringLiteral("options_demo"), STARTUP_DEMO, 0379 i18n ("&Demo At Start"), 0380 i18n ("Run a demo when the game starts."), 0381 i18n ("Run a demo when the game starts.")); 0382 0383 // Mouse Controls Hero 0384 // Keyboard Controls Hero 0385 // Laptop Hybrid 0386 // -------------------------- 0387 0388 KToggleAction * setMouse = settingAction (QStringLiteral("mouse_mode"), MOUSE, 0389 i18n ("&Mouse Controls Hero"), 0390 i18n ("Mouse controls hero."), 0391 i18n ("Use the mouse to control " 0392 "the hero's moves.")); 0393 0394 KToggleAction * setKeyboard = settingAction (QStringLiteral("keyboard_mode"), KEYBOARD, 0395 i18n ("&Keyboard Controls Hero"), 0396 i18n ("Keyboard controls hero."), 0397 i18n ("Use the keyboard to control " 0398 "the hero's moves.")); 0399 0400 KToggleAction * setLaptop = settingAction (QStringLiteral("laptop_mode"), LAPTOP, 0401 i18n ("Hybrid Control (&Laptop)"), 0402 i18n ("Pointer controls hero; dig " 0403 "using keyboard."), 0404 i18n ("Use the laptop's pointer device " 0405 "to control the hero's moves, and use " 0406 "the keyboard to dig left and right.")); 0407 0408 QActionGroup* controlGrp = new QActionGroup (this); 0409 controlGrp->addAction (setMouse); 0410 controlGrp->addAction (setKeyboard); 0411 controlGrp->addAction (setLaptop); 0412 controlGrp->setExclusive (true); 0413 0414 // Options within keyboard mode. 0415 // Click key to begin moving and continue moving indefinitely. 0416 // Click and hold key to begin moving: release key to stop. 0417 0418 KToggleAction * clickKey = settingAction (QStringLiteral("click_key"), CLICK_KEY, 0419 i18n ("&Click Key To Move"), 0420 i18n ("Click Key To Move."), 0421 i18n ("In keyboard mode, click a " 0422 "direction-key to start moving " 0423 "and keep on going until you " 0424 "click another key.")); 0425 0426 KToggleAction * holdKey = settingAction (QStringLiteral("hold_key"), HOLD_KEY, 0427 i18n ("&Hold Key To Move"), 0428 i18n ("Hold Key To Move."), 0429 i18n ("In keyboard mode, hold down a " 0430 "direction-key to move " 0431 "and release it to stop.")); 0432 0433 QActionGroup * keyGrp = new QActionGroup (this); 0434 keyGrp->addAction (clickKey); 0435 keyGrp->addAction (holdKey); 0436 keyGrp->setExclusive (true); 0437 0438 // Normal Speed 0439 // Beginner Speed 0440 // Champion Speed 0441 // Increase Speed 0442 // Decrease Speed 0443 // -------------------------- 0444 0445 KToggleAction * nSpeed = settingAction (QStringLiteral("normal_speed"), NORMAL_SPEED, 0446 i18n ("Normal Speed"), 0447 i18n ("Set normal speed."), 0448 i18n ("Set normal game speed.")); 0449 0450 KToggleAction * bSpeed = settingAction (QStringLiteral("beginner_speed"), 0451 BEGINNER_SPEED, 0452 i18n ("Beginner Speed"), 0453 i18n ("Set beginners' speed."), 0454 i18n ("Set beginners' game speed " 0455 "(0.5 times normal).")); 0456 0457 KToggleAction * cSpeed = settingAction (QStringLiteral("champion_speed"), 0458 CHAMPION_SPEED, 0459 i18n ("Champion Speed"), 0460 i18n ("Set champions' speed."), 0461 i18n ("Set champions' game speed " 0462 "(1.5 times normal).")); 0463 0464 a = gameAction (QStringLiteral("increase_speed"), INC_SPEED, 0465 i18n ("Increase Speed"), 0466 i18n ("Increase speed."), 0467 i18n ("Increase the game speed by 0.1 " 0468 "(maximum is 2.0 times normal)."), 0469 Qt::Key_Plus); 0470 0471 a = gameAction (QStringLiteral("decrease_speed"), DEC_SPEED, 0472 i18n ("Decrease Speed"), 0473 i18n ("Decrease speed."), 0474 i18n ("Decrease the game speed by 0.1 " 0475 "(minimum is 0.2 times normal)."), 0476 Qt::Key_Minus); 0477 0478 QActionGroup* speedGrp = new QActionGroup (this); 0479 speedGrp->addAction (nSpeed); 0480 speedGrp->addAction (bSpeed); 0481 speedGrp->addAction (cSpeed); 0482 speedGrp->setExclusive (true); 0483 0484 // Configure Shortcuts... 0485 // -------------------------- 0486 0487 KStandardAction::keyBindings ( 0488 this, &KGoldrunner::optionsConfigureKeys, 0489 actionCollection()); 0490 0491 /**************************************************************************/ 0492 /************************** KEYSTROKE ACTIONS **************************/ 0493 /**************************************************************************/ 0494 0495 // Two-handed KB controls and alternate one-handed controls for the hero. 0496 0497 // The actions for the movement keys are created but disabled. This lets 0498 // keyPressEvent() come through, instead of a signal, while still allowing 0499 // Settings->Configure Keys to change the key mappings. The keyPressEvent() 0500 // call is needed so that the key can be identified and matched to the 0501 // corresponding keyReleaseEvent() call and make the hold-key option 0502 // work correctly when two keys are held down simultaneously. 0503 0504 keyControl (QStringLiteral("stop"), i18n ("Stop"), Qt::Key_Space, STAND); 0505 keyControl (QStringLiteral("move_right"), i18n ("Move Right"), Qt::Key_Right, RIGHT, true); 0506 keyControl (QStringLiteral("move_left"), i18n ("Move Left"), Qt::Key_Left, LEFT, true); 0507 keyControl (QStringLiteral("move_up"), i18n ("Move Up"), Qt::Key_Up, UP, true); 0508 keyControl (QStringLiteral("move_down"), i18n ("Move Down"), Qt::Key_Down, DOWN, true); 0509 keyControl (QStringLiteral("dig_right"), i18n ("Dig Right"), Qt::Key_C, DIG_RIGHT); 0510 keyControl (QStringLiteral("dig_left"), i18n ("Dig Left"), Qt::Key_Z, DIG_LEFT); 0511 0512 // Alternate one-handed controls. Set up in "kgoldrunnerui.rc". 0513 0514 // Key_I, "move_up" 0515 // Key_L, "move_right" 0516 // Key_K, "move_down" 0517 // Key_J, "move_left" 0518 // Key_Space, "stop" (as above) 0519 // Key_O, "dig_right" 0520 // Key_U, "dig_left" 0521 0522 setupEditToolbarActions(); // Uses pixmaps from "renderer". 0523 0524 // Authors' debugging aids, effective when Pause is hit. Options include 0525 // stepping through the animation, toggling a debug patch or log messages 0526 // on or off during gameplay and printing the states of runners or tiles. 0527 0528 KConfigGroup debugGroup (KSharedConfig::openConfig(), QStringLiteral("Debugging")); 0529 bool addDebuggingShortcuts = debugGroup.readEntry 0530 ("DebuggingShortcuts", false); // Get debug option. 0531 if (! addDebuggingShortcuts) 0532 return; 0533 0534 keyControlDebug (QStringLiteral("do_step"), i18n ("Do a Step"), Qt::Key_Period, DO_STEP); 0535 keyControlDebug (QStringLiteral("bug_fix"), i18n ("Test Bug Fix"), Qt::Key_B, BUG_FIX); 0536 keyControlDebug (QStringLiteral("show_positions"), i18n ("Show Positions"), Qt::Key_W, S_POSNS); 0537 keyControlDebug (QStringLiteral("logging"), i18n ("Start Logging"), Qt::Key_G, LOGGING); 0538 keyControlDebug (QStringLiteral("show_hero"), i18n ("Show Hero"), Qt::Key_E, S_HERO); 0539 keyControlDebug (QStringLiteral("show_obj"), i18n ("Show Object"), Qt::Key_Slash, S_OBJ); 0540 0541 keyControlDebug (QStringLiteral("show_enemy_0"), i18n ("Show Enemy") + QLatin1Char('0'), Qt::Key_0, ENEMY_0); 0542 keyControlDebug (QStringLiteral("show_enemy_1"), i18n ("Show Enemy") + QLatin1Char('1'), Qt::Key_1, ENEMY_1); 0543 keyControlDebug (QStringLiteral("show_enemy_2"), i18n ("Show Enemy") + QLatin1Char('2'), Qt::Key_2, ENEMY_2); 0544 keyControlDebug (QStringLiteral("show_enemy_3"), i18n ("Show Enemy") + QLatin1Char('3'), Qt::Key_3, ENEMY_3); 0545 keyControlDebug (QStringLiteral("show_enemy_4"), i18n ("Show Enemy") + QLatin1Char('4'), Qt::Key_4, ENEMY_4); 0546 keyControlDebug (QStringLiteral("show_enemy_5"), i18n ("Show Enemy") + QLatin1Char('5'), Qt::Key_5, ENEMY_5); 0547 keyControlDebug (QStringLiteral("show_enemy_6"), i18n ("Show Enemy") + QLatin1Char('6'), Qt::Key_6, ENEMY_6); 0548 } 0549 0550 QAction * KGoldrunner::gameAction (const QString & name, 0551 const int code, 0552 const QString & text, 0553 const QString & toolTip, 0554 const QString & whatsThis, 0555 const QKeySequence & key) 0556 { 0557 QAction * ga = actionCollection()->addAction (name); 0558 ga->setText (text); 0559 ga->setToolTip (toolTip); 0560 ga->setWhatsThis (whatsThis); 0561 if (! key.isEmpty()) { 0562 KActionCollection::setDefaultShortcut(ga, key); 0563 } 0564 connect (ga, &QAction::triggered, this, [this, code] { game->gameActions(code); }); 0565 return ga; 0566 } 0567 0568 QAction * KGoldrunner::editAction (const QString & name, 0569 const int code, 0570 const QString & text, 0571 const QString & toolTip, 0572 const QString & whatsThis) 0573 { 0574 QAction * ed = actionCollection()->addAction (name); 0575 ed->setText (text); 0576 ed->setToolTip (toolTip); 0577 ed->setWhatsThis (whatsThis); 0578 connect (ed, &QAction::triggered, this, [this, code] { game->editActions(code); }); 0579 return ed; 0580 } 0581 0582 KToggleAction * KGoldrunner::settingAction (const QString & name, 0583 const int code, 0584 const QString & text, 0585 const QString & toolTip, 0586 const QString & whatsThis) 0587 { 0588 KToggleAction * s = new KToggleAction (text, this); 0589 actionCollection()->addAction (name, s); 0590 s->setToolTip (toolTip); 0591 s->setWhatsThis (whatsThis); 0592 connect (s, &QAction::triggered, this, [this, code] { game->settings(code); }); 0593 return s; 0594 } 0595 0596 KToggleAction * KGoldrunner::editToolbarAction (const QString & name, 0597 const char code, 0598 const QString & shortText, 0599 const QString & text, 0600 const QString & toolTip, 0601 const QString & whatsThis) 0602 { 0603 KToggleAction * ed = new KToggleAction (text, this); 0604 actionCollection()->addAction (name, ed); 0605 ed->setIconText (shortText); 0606 ed->setToolTip (toolTip); 0607 ed->setWhatsThis (whatsThis); 0608 connect (ed, &QAction::triggered, this, [this, code] { game->editToolbarActions(code); }); 0609 return ed; 0610 } 0611 0612 void KGoldrunner::keyControl (const QString & name, const QString & text, 0613 const QKeySequence & shortcut, const int code, 0614 const bool mover) 0615 { 0616 QAction * a = actionCollection()->addAction (name); 0617 a->setText (text); 0618 KActionCollection::setDefaultShortcut(a, shortcut); 0619 a->setAutoRepeat (false); // Avoid repeats of signals by QAction. 0620 0621 // If this is a move-key, let keyPressEvent() through, instead of signal. 0622 if (mover) { 0623 a->setEnabled (false); 0624 addAction (a); 0625 return; 0626 } 0627 0628 connect (a, &QAction::triggered, this, [this, code] { game->kbControl(code); }); 0629 addAction (a); 0630 } 0631 0632 void KGoldrunner::keyControlDebug (const QString & name, const QString & text, 0633 const QKeySequence & shortcut, const int code, 0634 const bool mover) 0635 { 0636 QAction * a = actionCollection()->addAction (name); 0637 a->setText (text); 0638 KActionCollection::setDefaultShortcut(a, shortcut); 0639 a->setAutoRepeat (false); // Avoid repeats of signals by QAction. 0640 0641 // If this is a move-key, let keyPressEvent() through, instead of signal. 0642 if (mover) { 0643 a->setEnabled (false); 0644 addAction (a); 0645 return; 0646 } 0647 0648 connect (a, &QAction::triggered, this, [this, code] { game->dbgControl(code); }); 0649 addAction (a); 0650 } 0651 0652 void KGoldrunner::setUpKeyboardControl() 0653 { 0654 // This is needed to ensure that the press and release of Up, Down, Left and 0655 // Right keys (arrow-keys) are all received as required. 0656 // 0657 // If the KGoldrunner widget does not have the keyboard focus, arrow-keys 0658 // provide only key-release events, which do not control the hero properly. 0659 // Other keys provide both press and release events, regardless of focus. 0660 0661 this->setFocusPolicy (Qt::StrongFocus); // Tab or click gets the focus. 0662 view->setFocusProxy (this); // So does a click on the play-area. 0663 this->setFocus (Qt::OtherFocusReason); // And we start by having the focus. 0664 } 0665 0666 void KGoldrunner::keyPressEvent (QKeyEvent * event) 0667 { 0668 // For movement keys, all presses and releases are processed, thus allowing 0669 // the hold-key option to work correctly when two keys are held down. 0670 0671 if (! identifyMoveAction (event, true)) { 0672 QWidget::keyPressEvent (event); 0673 } 0674 } 0675 0676 void KGoldrunner::keyReleaseEvent (QKeyEvent * event) 0677 { 0678 if (! identifyMoveAction (event, false)) { 0679 QWidget::keyReleaseEvent (event); 0680 } 0681 } 0682 0683 bool KGoldrunner::identifyMoveAction (QKeyEvent * event, bool pressed) 0684 { 0685 if (event->isAutoRepeat()) { 0686 return false; // Use only the release and the initial press. 0687 } 0688 Direction dirn = STAND; 0689 // The arrow keys show the "Keypad" modifier as being set, even if the 0690 // computer does NOT have a keypad (see Qt::KeypadModifier doco). It is 0691 // OK to ignore the Keypad modifier (see the code for "qevent.cpp" at 0692 // "bool QKeyEvent::matches(QKeySequence::StandardKey matchKey)"). The 0693 // keys on the keypad usually have equivalents on the main keyboard. 0694 QKeySequence keystroke (~(Qt::KeypadModifier) & 0695 (event->key() | event->modifiers())); 0696 if ((ACTION (QStringLiteral("move_left")))->shortcuts().contains(keystroke)) { 0697 dirn = LEFT; 0698 } 0699 else if ((ACTION (QStringLiteral("move_right")))->shortcuts().contains(keystroke)) { 0700 dirn = RIGHT; 0701 } 0702 else if ((ACTION (QStringLiteral("move_up")))->shortcuts().contains(keystroke)) { 0703 dirn = UP; 0704 } 0705 else if ((ACTION (QStringLiteral("move_down")))->shortcuts().contains(keystroke)) { 0706 dirn = DOWN; 0707 } 0708 else { 0709 return false; 0710 } 0711 // Use this event to control the hero, if KEYBOARD mode is selected. 0712 game->kbControl (dirn, pressed); 0713 return true; 0714 } 0715 0716 void KGoldrunner::viewFullScreen (bool activation) 0717 { 0718 KToggleFullScreenAction::setFullScreen (this, activation); 0719 } 0720 0721 void KGoldrunner::gameFreeze (bool on_off) 0722 { 0723 myPause->setChecked (on_off); 0724 frozen = on_off; // Remember the state (for the configure-keys case). 0725 QStringList pauseKeys; 0726 0727 const auto keyBindings = myPause->shortcuts(); 0728 for (const QKeySequence &s : keyBindings) { 0729 pauseKeys.append(s.toString(QKeySequence::NativeText)); 0730 } 0731 0732 QString msg; 0733 if (on_off) { 0734 if (pauseKeys.isEmpty()) { 0735 msg = i18n("The game is paused"); 0736 } else if (pauseKeys.size() == 1) { 0737 msg = i18n("Press \"%1\" to RESUME", pauseKeys.at(0)); 0738 } else { 0739 msg = i18n("Press \"%1\" or \"%2\" to RESUME", pauseKeys.at(0), 0740 pauseKeys.at(1)); 0741 } 0742 } else { 0743 if (pauseKeys.isEmpty()) { 0744 msg = QString(); 0745 } else if (pauseKeys.size() == 1) { 0746 msg = i18n("Press \"%1\" to PAUSE", pauseKeys.at(0)); 0747 } else { 0748 msg = i18n("Press \"%1\" or \"%2\" to PAUSE", pauseKeys.at(0), 0749 pauseKeys.at(1)); 0750 } 0751 } 0752 scene->setPauseResumeText (msg); 0753 } 0754 0755 void KGoldrunner::adjustHintAction (bool hintAvailable) 0756 { 0757 hintAction->setEnabled (hintAvailable); 0758 0759 QString msg; 0760 msg = hintAvailable ? i18n("Has hint") : i18n("No hint"); 0761 scene->setHasHintText (msg); 0762 } 0763 0764 void KGoldrunner::setToggle (const QString &actionName, const bool onOff) 0765 { 0766 ((KToggleAction *) ACTION (actionName))->setChecked (onOff); 0767 } 0768 0769 void KGoldrunner::setAvail (const QString &actionName, const bool onOff) 0770 { 0771 ((QAction *) ACTION (actionName))->setEnabled (onOff); 0772 } 0773 0774 void KGoldrunner::setEditMenu (bool on_off) 0775 { 0776 saveEdits->setEnabled (on_off); 0777 0778 saveGame->setEnabled (! on_off); 0779 hintAction->setEnabled (! on_off); 0780 killHero->setEnabled (! on_off); 0781 highScore->setEnabled (! on_off); 0782 setAvail (QStringLiteral("instant_replay"), (! on_off)); 0783 setAvail (QStringLiteral("game_pause"), (! on_off)); 0784 0785 if (on_off){ 0786 // Set the editToolbar icons to the current tile-size. 0787 //qCDebug(KGOLDRUNNER_LOG) << "ToolBar icon size:" << scene->tileSize (); 0788 toolBar (QStringLiteral("editToolbar"))->setIconSize (scene->tileSize ()); 0789 0790 // Set the editToolbar icons up with pixmaps of the current theme. 0791 setEditIcon (QStringLiteral("brickbg"), BRICK); 0792 setEditIcon (QStringLiteral("fbrickbg"), FBRICK); 0793 setEditIcon (QStringLiteral("freebg"), FREE); 0794 setEditIcon (QStringLiteral("nuggetbg"), NUGGET); 0795 setEditIcon (QStringLiteral("polebg"), BAR); 0796 setEditIcon (QStringLiteral("concretebg"), CONCRETE); 0797 setEditIcon (QStringLiteral("ladderbg"), LADDER); 0798 setEditIcon (QStringLiteral("hladderbg"), HLADDER); 0799 setEditIcon (QStringLiteral("edherobg"), HERO); 0800 setEditIcon (QStringLiteral("edenemybg"), ENEMY); 0801 setToggle (QStringLiteral("brickbg"), true); // Default edit-object is BRICK. 0802 0803 toolBar (QStringLiteral("editToolbar"))->show(); 0804 } 0805 else { 0806 toolBar (QStringLiteral("editToolbar"))->hide(); 0807 } 0808 } 0809 0810 void KGoldrunner::setEditIcon (const QString & actionName, const char iconType) 0811 { 0812 ((KToggleAction *) (actionCollection()->action (actionName)))-> 0813 setIcon (QIcon(renderer->getPixmap (iconType))); 0814 } 0815 0816 /******************************************************************************/ 0817 /******************* SLOTS FOR MENU AND KEYBOARD ACTIONS *******************/ 0818 /******************************************************************************/ 0819 0820 void KGoldrunner::changeTheme () 0821 { 0822 renderer->selectTheme (); 0823 } 0824 0825 void KGoldrunner::redrawEditToolbar () 0826 { 0827 // Signalled by the scene after the theme or tile size has changed. 0828 if (game->inEditMode()) { 0829 setEditMenu (true); 0830 } 0831 } 0832 0833 void KGoldrunner::saveProperties (KConfigGroup & /* config - unused */) 0834 { 0835 // The 'config' object points to the session managed 0836 // config file. Anything you write here will be available 0837 // later when this app is restored. 0838 0839 //qCDebug(KGOLDRUNNER_LOG) << "I am in KGoldrunner::saveProperties."; 0840 } 0841 0842 void KGoldrunner::readProperties (const KConfigGroup & /* config - unused */) 0843 { 0844 // The 'config' object points to the session managed 0845 // config file. This function is automatically called whenever 0846 // the app is being restored. Read in here whatever you wrote 0847 // in 'saveProperties' 0848 0849 //qCDebug(KGOLDRUNNER_LOG) << "I am in KGoldrunner::readProperties."; 0850 } 0851 0852 void KGoldrunner::optionsConfigureKeys() 0853 { 0854 auto *dlg = new KShortcutsDialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); 0855 dlg->addCollection(actionCollection()); 0856 dlg->setAttribute(Qt::WA_DeleteOnClose); 0857 connect(dlg, &KShortcutsDialog::saved, this, [this]() { 0858 gameFreeze(frozen); 0859 }); 0860 0861 dlg->configure(true /* save settings */); 0862 } 0863 0864 bool KGoldrunner::getDirectories() 0865 { 0866 bool result = true; 0867 0868 QString myDir = QStringLiteral("kgoldrunner"); 0869 QStringList genericDataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); 0870 QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); 0871 0872 // Find the system collections in a directory of the required KDE type. 0873 systemDataDir = QStandardPaths::locate(QStandardPaths::AppDataLocation, 0874 QStringLiteral("system/") 0875 , QStandardPaths::LocateDirectory); 0876 if (systemDataDir.length() <= 0) { 0877 KGrMessage::information (this, i18n ("Get Folders"), 0878 i18n ("Cannot find system games sub-folder '/system/' " 0879 "in areas '%1'.", 0880 appDataLocations.join(QLatin1Char(';')))); 0881 result = false; // ABORT if the games data is missing. 0882 } 0883 0884 // Locate and optionally create directories for user collections and levels. 0885 userDataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/'); 0886 QString levelDir = userDataDir + QStringLiteral("levels"); 0887 KIO::mkpath(QUrl::fromUserInput(levelDir))->exec(); 0888 0889 return (result); 0890 } 0891 0892 // This method is invoked when the main window is closed, whether by selecting 0893 // "Quit" from the menu or by clicking the "X" at the top right of the window. 0894 0895 // If we return true, game-edits were saved or abandoned or there was no editing 0896 // in progress, so the main window will close. If we return false, the user has 0897 // asked to go on editing, so the main window stays open. 0898 0899 bool KGoldrunner::queryClose() 0900 { 0901 return (game->saveOK()); 0902 } 0903 0904 /******************************************************************************/ 0905 /********************** MAKE A TOOLBAR FOR THE EDITOR **********************/ 0906 /******************************************************************************/ 0907 0908 void KGoldrunner::setupEditToolbarActions() 0909 { 0910 QAction * ed = editAction (QStringLiteral("edit_hint"), EDIT_HINT, 0911 i18n ("Edit Name/Hint"), 0912 i18n ("Edit level name or hint"), 0913 i18n ("Edit text for the name or hint " 0914 "of a level")); 0915 ed->setIcon (QIcon::fromTheme( QStringLiteral( "games-hint" ))); 0916 ed->setIconText (i18n ("Name/Hint")); 0917 0918 KToggleAction * free = editToolbarAction (QStringLiteral("freebg"), FREE, 0919 i18n ("Erase"), i18n ("Space/Erase"), 0920 i18n ("Paint empty squares or erase"), 0921 i18n ("Erase objects or paint empty squares")); 0922 0923 KToggleAction * edhero = editToolbarAction (QStringLiteral("edherobg"), HERO, 0924 i18n("Hero"), i18n ("Hero"), 0925 i18n ("Move hero"), 0926 i18n ("Change the hero's starting position")); 0927 0928 KToggleAction * edenemy = editToolbarAction (QStringLiteral("edenemybg"), ENEMY, 0929 i18n ("Enemy"), i18n ("Enemy"), 0930 i18n ("Paint enemies"), 0931 i18n ("Paint enemies at their starting positions") 0932 ); 0933 0934 KToggleAction * brick = editToolbarAction (QStringLiteral("brickbg"), BRICK, 0935 i18n ("Brick"), i18n ("Brick"), 0936 i18n ("Paint bricks (can dig)"), 0937 i18n ("Paint bricks (diggable objects)")); 0938 0939 KToggleAction* concrete = editToolbarAction (QStringLiteral("concretebg"), CONCRETE, 0940 i18n ("Concrete"), i18n ("Concrete"), 0941 i18n ("Paint concrete (cannot dig)"), 0942 i18n ("Paint concrete objects (not diggable)")); 0943 0944 KToggleAction * fbrick = editToolbarAction (QStringLiteral("fbrickbg"), FBRICK, 0945 i18n ("Trap"), i18n ("Trap/False Brick"), 0946 i18n ("Paint traps or false bricks " 0947 "(can fall through)"), 0948 i18n ("Paint traps or false bricks " 0949 "(can fall through)")); 0950 0951 KToggleAction * ladder = editToolbarAction (QStringLiteral("ladderbg"), LADDER, 0952 i18n ("Ladder"), i18n ("Ladder"), 0953 i18n ("Paint ladders"), 0954 i18n ("Paint ladders (ways to go up or down)")); 0955 0956 KToggleAction * hladder = editToolbarAction (QStringLiteral("hladderbg"), HLADDER, 0957 i18n ("H Ladder"), i18n ("Hidden Ladder"), 0958 i18n ("Paint hidden ladders"), 0959 i18n ("Paint hidden ladders, which appear " 0960 "when all the gold is gone")); 0961 0962 KToggleAction * bar = editToolbarAction (QStringLiteral("polebg"), BAR, 0963 i18n ("Bar"), i18n ("Bar/Pole"), 0964 i18n ("Paint bars or poles"), 0965 i18n ("Paint bars or poles (can fall from these)") 0966 ); 0967 0968 KToggleAction * nugget = editToolbarAction (QStringLiteral("nuggetbg"), NUGGET, 0969 i18n ("Gold"), i18n ("Gold/Treasure"), 0970 i18n ("Paint gold (or other treasure)"), 0971 i18n ("Paint gold pieces (or other treasure)")); 0972 0973 QActionGroup* editButtons = new QActionGroup (this); 0974 editButtons->setExclusive (true); 0975 editButtons->addAction (free); 0976 editButtons->addAction (edhero); 0977 editButtons->addAction (edenemy); 0978 editButtons->addAction (brick); 0979 editButtons->addAction (concrete); 0980 editButtons->addAction (fbrick); 0981 editButtons->addAction (ladder); 0982 editButtons->addAction (hladder); 0983 editButtons->addAction (bar); 0984 editButtons->addAction (nugget); 0985 0986 brick->setChecked (true); 0987 } 0988 0989 QSize KGoldrunner::sizeHint() const 0990 { 0991 return QSize (640, 600); 0992 } 0993 0994 #include "moc_kgoldrunner.cpp"