1/*
2 * Copyright 2006-2009 Parker Coates <coates@kde.org>
3 *
4 * This file is part of Killbots.
5 *
6 * Killbots is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Killbots is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Killbots. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "mainwindow.h"
21
22#include "coordinator.h"
23#include "engine.h"
24#include "optionspage.h"
25#include "renderer.h"
26#include "rulesetselector.h"
27#include "scene.h"
28#include "settings.h"
29#include "view.h"
30
31#include <KgThemeProvider>
32#include <KgThemeSelector>
33#include <highscore/kscoredialog.h>
34#include <kstandardgameaction.h>
35
36#include <KDE/KAction>
37#include <KDE/KActionCollection>
38#include <KDE/KApplication>
39#include <KDE/KConfigDialog>
40#include <KDE/KLocalizedString>
41#include <KDE/KMessageBox>
42#include <KDE/KShortcutsDialog>
43#include <KDE/KStandardAction>
44#include <KDE/KStandardDirs>
45
46#include <QtCore/QSignalMapper>
47#include <QtCore/QTimer>
48
49Killbots::MainWindow::MainWindow( QWidget * parent )
50 : KXmlGuiWindow( parent ),
51 m_scoreDialog( 0 ),
52 m_rulesetChanged( false )
53{
54 setAcceptDrops(false);
55
56 m_coordinator = new Coordinator( this );
57 m_coordinator->setAnimationSpeed( Settings::animationSpeed() );
58
59 m_engine = new Engine( m_coordinator, this );
60 m_coordinator->setEngine( m_engine );
61 connect( m_engine, SIGNAL(gameOver(int,int)), this, SLOT(onGameOver(int,int)) );
62
63 m_scene = new Scene( this );
64 m_coordinator->setScene( m_scene );
65 connect( m_scene, SIGNAL(clicked(int)), m_coordinator, SLOT(requestAction(int)) );
66 connect( Renderer::self()->themeProvider(), SIGNAL(currentThemeChanged(const KgTheme*)), m_scene, SLOT(doLayout()) );
67
68 m_view = new View( m_scene, this );
69 m_view->setMinimumSize( 400, 280 );
70 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.") );
71 setCentralWidget( m_view );
72 connect( m_view, SIGNAL(sizeChanged(QSize)), m_scene, SLOT(doLayout()) );
73
74 m_keyboardMapper = new QSignalMapper( this );
75 connect( m_keyboardMapper, SIGNAL(mapped(int)), m_coordinator, SLOT(requestAction(int)) );
76
77 setupActions();
78 connect( m_engine, SIGNAL(teleportAllowed(bool)), actionCollection()->action("teleport"), SLOT(setEnabled(bool)) );
79 connect( m_engine, SIGNAL(teleportAllowed(bool)), actionCollection()->action("teleport_sip"), SLOT(setEnabled(bool)) );
80 connect( m_engine, SIGNAL(teleportSafelyAllowed(bool)), actionCollection()->action("teleport_safely"), SLOT(setEnabled(bool)) );
81 connect( m_engine, SIGNAL(vaporizerAllowed(bool)), actionCollection()->action("vaporizer"), SLOT(setEnabled(bool)) );
82 connect( m_engine, SIGNAL(waitOutRoundAllowed(bool)), actionCollection()->action("wait_out_round"), SLOT(setEnabled(bool)) );
83
84 setupGUI( Save | Create | ToolBar | Keys );
85
86 // Delaying the start of the first game by 50ms to avoid resize events after
87 // the game has been started. Delaying by 0ms doesn't seem to be enough.
88 QTimer::singleShot( 50, m_coordinator, SLOT(requestNewGame()) );
89}
90
91
92Killbots::MainWindow::~MainWindow()
93{
94 Killbots::Renderer::cleanup();
95}
96
97
98void Killbots::MainWindow::configurePreferences()
99{
100 // An instance of the dialog could be already created and could be cached,
101 // in which case we want to display the cached dialog instead of creating
102 // another one
103 if ( !KConfigDialog::showDialog( "configurePreferencesDialog" ) )
104 {
105 // Creating a dialog because KConfigDialog didn't find an instance of it
106 KConfigDialog * configDialog = new KConfigDialog( this, "configurePreferencesDialog", Settings::self() );
107
108 // Creating setting pages and adding them to the dialog
109 configDialog->addPage( new OptionsPage( this ),
110 i18n("General"),
111 "configure",
112 i18n("Configure general settings")
113 );
114 configDialog->addPage( new RulesetSelector( this ),
115 i18n("Game Type"),
116 "games-config-custom",
117 i18n("Select a game type")
118 );
119 configDialog->addPage( new KgThemeSelector(Renderer::self()->themeProvider()),
120 i18n("Appearance"),
121 "games-config-theme",
122 i18n("Select a graphical theme")
123 );
124
125 configDialog->setMaximumSize( 800, 600 );
126 configDialog->setInitialSize( QSize( 600, 450 ) );
127
128 // Update the sprite style if it has changed
129 connect( configDialog, SIGNAL(settingsChanged(QString)), this, SLOT(onSettingsChanged()) );
130 connect( configDialog, SIGNAL(finished()), this, SLOT(onConfigDialogClosed()) );
131
132 configDialog->show();
133 }
134}
135
136
137void Killbots::MainWindow::onSettingsChanged()
138{
139 m_coordinator->setAnimationSpeed( Settings::animationSpeed() );
140
141 if ( m_engine->ruleset()->fileName() != Settings::ruleset() )
142 {
143 kDebug() << "Detected a changed in ruleset. From" << m_engine->ruleset()->fileName() << "to" << Settings::ruleset();
144
145 // We don't act on the changed ruleset here because the config dialog
146 // is still visible. We want the game in progress to be visible when
147 // we display the message box. We also don't want a new game to start
148 // while hidden behind the config dialog. So we wait until the config
149 // dialog is closed. See onConfigDialogClosed below.
150 m_rulesetChanged = true;
151 }
152}
153
154
155void Killbots::MainWindow::onConfigDialogClosed()
156{
157 if ( m_rulesetChanged )
158 {
159 if ( !m_engine->gameHasStarted()
160 || KMessageBox::questionYesNo( this,
161 i18n("A new game type has been selected, but there is already a game in progress."),
162 i18n("Game Type Changed"),
163 KGuiItem( i18n("Continue Current Game") ),
164 KGuiItem( i18n("Start a New Game") )
165 ) == KMessageBox::No
166 )
167 {
168 m_coordinator->requestNewGame();
169 }
170
171 m_rulesetChanged = false;
172 }
173}
174
175
176void Killbots::MainWindow::createScoreDialog()
177{
178 m_scoreDialog = new KScoreDialog( KScoreDialog::Name, this );
179 m_scoreDialog->addField( KScoreDialog::Level, i18n("Round"), "round" );
180 m_scoreDialog->setModal( false );
181
182 QStringList fileList;
183 KGlobal::dirs()->findAllResources ( "ruleset", "*.desktop", KStandardDirs::NoDuplicates, fileList );
184 foreach ( const QString & fileName, fileList )
185 {
186 const Ruleset * ruleset = Ruleset::load( fileName );
187 if ( ruleset )
188 m_scoreDialog->addLocalizedConfigGroupName( qMakePair( ruleset->scoreGroupKey(), ruleset->name() ) );
189 delete ruleset;
190 }
191}
192
193
194void Killbots::MainWindow::onGameOver( int score, int round )
195{
196 if ( score && m_engine->ruleset() )
197 {
198 if ( !m_scoreDialog )
199 createScoreDialog();
200
201 m_scoreDialog->setConfigGroup( qMakePair( m_engine->ruleset()->scoreGroupKey(), m_engine->ruleset()->name() ) );
202
203 KScoreDialog::FieldInfo scoreEntry;
204 scoreEntry[ KScoreDialog::Score ].setNum( score );
205 scoreEntry[ KScoreDialog::Level ].setNum( round );
206
207 if ( m_scoreDialog->addScore( scoreEntry ) )
208 QTimer::singleShot( 1000, this, SLOT(showHighscores()) );
209 }
210}
211
212
213void Killbots::MainWindow::showHighscores()
214{
215 if ( !m_scoreDialog )
216 createScoreDialog();
217
218 m_scoreDialog->exec();
219}
220
221
222KAction * Killbots::MainWindow::createMappedAction( int mapping,
223 const QString & internalName,
224 const QString & displayName,
225 const QString & translatedShortcut,
226 const QKeySequence & alternateShortcut,
227 const QString & helpText,
228 const QString & icon
229 )
230{
231 KAction * action = new KAction( displayName, actionCollection() );
232 action->setObjectName( internalName );
233 action->setShortcut( KShortcut( QKeySequence( translatedShortcut ), alternateShortcut ) );
234 if ( !helpText.isEmpty() )
235 action->setHelpText( helpText );
236 if ( !icon.isEmpty() )
237 action->setIcon( KIcon( icon ) );
238
239 connect( action, SIGNAL(triggered()), m_keyboardMapper, SLOT(map()) );
240 m_keyboardMapper->setMapping( action, mapping );
241 actionCollection()->addAction( internalName, action );
242
243 return action;
244}
245
246
247void Killbots::MainWindow::setupActions()
248{
249 KStandardGameAction::gameNew( m_coordinator, SLOT(requestNewGame()), actionCollection() );
250 KStandardGameAction::highscores( this, SLOT(showHighscores()), actionCollection() );
251 KStandardGameAction::quit( kapp, SLOT(quit()), actionCollection() );
252 KStandardAction::preferences( this, SLOT(configurePreferences()), actionCollection() );
253
254 createMappedAction( TeleportSafely,
255 "teleport_safely",
256 i18n("Teleport Safely"),
257 i18nc("Shortcut for teleport safely. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "T"),
258 Qt::Key_Plus,
259 i18n("Teleport to a safe location"),
260 "games-solve"
261 );
262 createMappedAction( Teleport,
263 "teleport",
264 i18n("Teleport"),
265 i18nc("Shortcut for teleport. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "R"),
266 Qt::Key_Minus,
267 i18n("Teleport to a random location"),
268 "roll"
269 );
270 createMappedAction( TeleportSafelyIfPossible,
271 "teleport_sip",
272 i18n("Teleport, Safely If Possible"),
273 i18nc("Shortcut for teleport safely if possible. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "Space"),
274 Qt::Key_0,
275 i18n("Teleport safely if that action is enabled, otherwise teleport randomly")
276 );
277 createMappedAction( Vaporizer,
278 "vaporizer",
279 i18n("Vaporizer"),
280 i18nc("Shortcut for vaporizer. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "F"),
281 Qt::Key_Period,
282 i18n("Destroy all enemies in neighboring cells"),
283 "edit-bomb"
284 );
285 createMappedAction( WaitOutRound,
286 "wait_out_round",
287 i18n("Wait Out Round"),
288 i18nc("Shortcut for wait out round. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "V"),
289 Qt::Key_Asterisk,
290 i18n("Risk remaining in place until the end of the round for bonuses"),
291 "process-stop"
292 );
293 createMappedAction( UpLeft,
294 "move_up_left",
295 i18n("Move Up and Left"),
296 i18nc("Shortcut for move up and left. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "Q"),
297 Qt::Key_7
298 );
299 createMappedAction( Up,
300 "move_up",
301 i18n("Move Up"),
302 i18nc("Shortcut for move up. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "W"),
303 Qt::Key_8
304 );
305 createMappedAction( UpRight,
306 "move_up_right",
307 i18n("Move Up and Right"),
308 i18nc("Shortcut for move up and right. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "E"),
309 Qt::Key_9
310 );
311 createMappedAction( Left,
312 "move_left",
313 i18n("Move Left"),
314 i18nc("Shortcut for move left. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "A"),
315 Qt::Key_4
316 );
317 createMappedAction( Hold,
318 "stand_still",
319 i18n("Stand Still"),
320 i18nc("Shortcut for stand still. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "S"),
321 Qt::Key_5
322 );
323 createMappedAction( Right,
324 "move_right",
325 i18n("Move Right"),
326 i18nc("Shortcut for move right. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "D"),
327 Qt::Key_6
328 );
329 createMappedAction( DownLeft,
330 "move_down_left",
331 i18n("Move Down and Left"),
332 i18nc("Shortcut for move down and left. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "Z"),
333 Qt::Key_1
334 );
335 createMappedAction( Down,
336 "move_down",
337 i18n("Move Down"),
338 i18nc("Shortcut for move down. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "X"),
339 Qt::Key_2
340 );
341 createMappedAction( DownRight,
342 "move_down_right",
343 i18n("Move Down and Right"),
344 i18nc("Shortcut for move down and right. See http://websvn.kde.org/trunk/KDE/kdegames/killbots/README.translators?view=markup", "C"),
345 Qt::Key_3
346 );
347}
348
349#include "moc_mainwindow.cpp"
350