1 /*
2     This file is part of the game 'KTron'
3 
4     SPDX-FileCopyrightText: 1998-2000 Matthias Kiefer <matthias.kiefer@gmx.de>
5     SPDX-FileCopyrightText: 2005 Benjamin C. Meyer <ben at meyerhome dot net>
6     SPDX-FileCopyrightText: 2008-2009 Stas Verberkt <legolas at legolasweb dot nl>
7 
8     SPDX-License-Identifier: GPL-2.0-or-later
9 
10 */
11 
12 #include "ktron.h"
13 #include "renderer.h"
14 #include "settings.h"
15 #include "ui_general.h"
16 
17 #include <QApplication>
18 #include <QStatusBar>
19 
20 #include <KActionCollection>
21 #include <KConfigDialog>
22 #include <KgDifficulty>
23 #include <KLocalizedString>
24 #include <KMessageBox>
25 #include <KStandardGameAction>
26 #include <KScoreDialog>
27 #include <KShortcutsDialog>
28 #include <KToggleAction>
29 
30 #define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API
31 #include <libkdegamesprivate/kgamethemeselector.h>
32 
33 //UI
34 class General : public QWidget, public Ui::General
35 {
36 public:
General(QWidget * parent=nullptr)37     explicit General(QWidget *parent = nullptr)
38         : QWidget(parent)
39         {
40             setupUi(this);
41         }
42 };
43 
44 /**
45  * Constructor
46  */
KTron(QWidget * parent)47 KTron::KTron(QWidget *parent) : KXmlGuiWindow(parent, Qt::WindowFlags()) {
48 	m_tron = new Tron(this);
49 	connect(m_tron, &Tron::gameEnds, this, &KTron::changeStatus);
50 	connect(m_tron, &Tron::updatedScore, this, &KTron::updateScore);
51 	connect(m_tron, &Tron::pauseBlocked, this, &KTron::blockPause);
52 	m_tron->setMinimumSize(700,420);
53 	setCentralWidget(m_tron);
54 
55 	// create statusbar
56 	for (auto &label : m_statusBarLabel) {
57 		label = new QLabel(this);
58 		label->setAlignment(Qt::AlignCenter);
59 		statusBar()->addWidget(label, 1);
60 	}
61 
62 	// We match up keyboard events ourselves in Tron::keyPressEvent()
63 	// We must disable the actions, otherwise we don't get the keyPressEvent's
64 
65 	m_player0Up = actionCollection()->addAction( QStringLiteral( "Pl1Up" ));
66 	m_player0Up->setText(i18n("Right Player / KSnake mode: Up"));
67 	actionCollection()->setDefaultShortcut(m_player0Up,Qt::Key_Up);
68 	connect(m_player0Up, &QAction::triggered, this, &KTron::triggerKey0Up);
69 	addAction(m_player0Up);
70 
71 	m_player0Down = actionCollection()->addAction( QStringLiteral( "Pl1Down" ));
72 	m_player0Down->setText(i18n("Right Player / KSnake mode: Down"));
73 	actionCollection()->setDefaultShortcut(m_player0Down,Qt::Key_Down);
74 	connect(m_player0Down, &QAction::triggered, this, &KTron::triggerKey0Down);
75 	addAction(m_player0Down);
76 
77 	m_player0Right = actionCollection()->addAction( QStringLiteral( "Pl1Right" ));
78 	m_player0Right->setText(i18n("Right Player / KSnake mode: Right"));
79 	actionCollection()->setDefaultShortcut(m_player0Right,Qt::Key_Right);
80 	connect(m_player0Right, &QAction::triggered, this, &KTron::triggerKey0Right);
81 	addAction(m_player0Right);
82 
83 	m_player0Left = actionCollection()->addAction( QStringLiteral( "Pl1Left" ));
84 	m_player0Left->setText(i18n("Right Player / KSnake mode: Left"));
85 	actionCollection()->setDefaultShortcut(m_player0Left,Qt::Key_Left);
86 	connect(m_player0Left, &QAction::triggered, this, &KTron::triggerKey0Left);
87 	addAction(m_player0Left);
88 
89 	m_player0Accelerate = actionCollection()->addAction( QStringLiteral( "Pl1Ac" ));
90 	m_player0Accelerate->setText(i18n("Right Player: Accelerator"));
91 	actionCollection()->setDefaultShortcut(m_player0Accelerate,Qt::Key_0);
92 	m_player0Accelerate->setEnabled(false); // Alternate handling, because of up/down events
93 	addAction(m_player0Accelerate);
94 
95 	m_player1Up = actionCollection()->addAction( QStringLiteral( "Pl2Up" ));
96 	m_player1Up->setText(i18n("Left Player: Up"));
97 	actionCollection()->setDefaultShortcut(m_player1Up,Qt::Key_W);
98 	connect(m_player1Up, &QAction::triggered, this, &KTron::triggerKey1Up);
99 	addAction(m_player1Up);
100 
101 	m_player1Down = actionCollection()->addAction( QStringLiteral( "Pl2Down" ));
102 	m_player1Down->setText(i18n("Left Player: Down"));
103 	actionCollection()->setDefaultShortcut(m_player1Down,Qt::Key_S);
104 	connect(m_player1Down, &QAction::triggered, this, &KTron::triggerKey1Down);
105 	addAction(m_player1Down);
106 
107 	m_player1Right = actionCollection()->addAction( QStringLiteral( "Pl2Right" ));;
108 	m_player1Right->setText(i18n("Left Player: Right"));
109 	actionCollection()->setDefaultShortcut(m_player1Right,Qt::Key_D);
110 	connect(m_player1Right, &QAction::triggered, this, &KTron::triggerKey1Right);
111 	addAction(m_player1Right);
112 
113 	m_player1Left = actionCollection()->addAction( QStringLiteral( "Pl2Left" ));
114 	m_player1Left->setText(i18n("Left Player: Left"));
115 	actionCollection()->setDefaultShortcut(m_player1Left,Qt::Key_A);
116 	connect(m_player1Left, &QAction::triggered, this, &KTron::triggerKey1Left);
117 	addAction(m_player1Left);
118 
119 	m_player1Accelerate = actionCollection()->addAction( QStringLiteral( "Pl2Ac" ));
120 	m_player1Accelerate->setText(i18n("Left Player: Accelerator"));
121 	actionCollection()->setDefaultShortcut(m_player1Accelerate,Qt::Key_Q);
122 	m_player1Accelerate->setEnabled(false); // Alternate handling, because of up/down events
123 	addAction(m_player1Accelerate);
124 
125 	// Pause
126 	m_pauseButton = KStandardGameAction::pause(m_tron, &Tron::togglePause, actionCollection());
127 	m_pauseButton->setEnabled(false);
128 	// New
129 	KStandardGameAction::gameNew(m_tron, &Tron::newGame, actionCollection());
130 	// Quit
131 	KStandardGameAction::quit(qApp, &QApplication::quit, actionCollection());
132 	// Settings
133 	KStandardAction::preferences(this, &KTron::showSettings, actionCollection());
134 	// Configure keys
135 	KStandardAction::keyBindings(this, &KTron::optionsConfigureKeys, actionCollection());
136 	// Highscores
137 	KStandardGameAction::highscores(this, &KTron::showHighscores, actionCollection());
138 
139 	//difficulty
140     Kg::difficulty()->addStandardLevelRange(
141         KgDifficultyLevel::VeryEasy, KgDifficultyLevel::VeryHard,
142         KgDifficultyLevel::Easy //default
143     );
144     KgDifficultyGUI::init(this);
145     connect(Kg::difficulty(), &KgDifficulty::currentLevelChanged, m_tron, &Tron::loadSettings);
146 
147 	setupGUI( KXmlGuiWindow::Keys | StatusBar | Save | Create);
148 	loadSettings();
149 	m_tron->loadSettings();
150 }
151 
~KTron()152 KTron::~KTron()
153 {
154 	delete m_tron;
155 }
156 
loadSettings()157 void KTron::loadSettings() {
158 	if (!Renderer::self()->loadTheme(Settings::theme()))
159 	{
160 		KMessageBox::error(this, i18n("Failed to load \"%1\" theme. Please check your installation.", Settings::theme()));
161 	}
162 
163 	m_tron->getPlayer(0)->setName(Settings::namePlayer1());
164 	m_tron->getPlayer(1)->setName(Settings::namePlayer2());
165 	Settings::setNamePlayer1(m_tron->getPlayer(0)->getName());
166 	if (!m_tron->getPlayer(1)->isComputer()) {
167 		Settings::setNamePlayer2(m_tron->getPlayer(1)->getName());
168 	}
169 
170 	updateStatusbar();
171 }
172 
updateStatusbar()173 void KTron::updateStatusbar() {
174 	QString message;
175 
176 	if (!m_tron->running() && m_tron->hasWinner()) {
177 		QString winnerName = m_tron->getPlayer(m_tron->getWinner())->getName();
178 
179 		message = i18n("%1 has won!", winnerName);
180 	}
181 	else if (m_tron->paused()) {
182 		message = i18n("Game paused");
183 	}
184 	else {
185 		message = QString();
186 	}
187 
188 	m_statusBarLabel[0]->setText(message);
189 
190 	if (Settings::gameType() == Settings::EnumGameType::Snake)
191 	{
192 		QString string = QStringLiteral( "%1: %2").arg(m_tron->getPlayer(0)->getName()).arg(m_tron->getPlayer(0)->getScore());
193 		m_statusBarLabel[1]->setText(string);
194 		m_statusBarLabel[2]->clear();
195 	}
196 	else
197 	{
198 		for (int i = 0; i < 2; ++i) {
199 			QString name = m_tron->getPlayer(1 - i)->getName();
200 			int score = m_tron->getPlayer(1 - i)->getScore();
201 
202 			QString string = QStringLiteral( "%1: %2").arg(name).arg(score);
203 			m_statusBarLabel[i+1]->setText(string);
204 		}
205 	}
206 }
207 
blockPause(bool block)208 void KTron::blockPause(bool block)
209 {
210 	//qCDebug(KSNAKEDUEL_LOG) << "Setting pause button state to: "  << !block;
211 	m_pauseButton->setEnabled(!block);
212 }
213 
updateScore()214 void KTron::updateScore()
215 {
216 	updateStatusbar();
217 }
218 
changeStatus()219 void KTron::changeStatus() {
220 	updateStatusbar();
221 
222 	if (Settings::gameType() == Settings::EnumGameType::Snake)
223 	{
224 		KScoreDialog scoreDialog(KScoreDialog::Score | KScoreDialog::Name, this);
225 		scoreDialog.initFromDifficulty(Kg::difficulty());
226 
227 		KScoreDialog::FieldInfo scoreInfo;
228 		scoreInfo[KScoreDialog::Name] = m_tron->getPlayer(0)->getName();
229 		scoreInfo[KScoreDialog::Score].setNum(m_tron->getPlayer(0)->getScore());
230 		if (scoreDialog.addScore(scoreInfo) != 0)
231 			scoreDialog.exec();
232 	}
233 }
234 
paletteChange(const QPalette &)235 void KTron::paletteChange(const QPalette &){
236    update();
237    m_tron->updatePixmap();
238    m_tron->update();
239 }
240 
241 /**
242  * Show Settings dialog.
243  */
showSettings()244 void KTron::showSettings(){
245 	if (KConfigDialog::showDialog(QStringLiteral( "settings" )))
246 		return;
247 
248 	m_generalConfigDialog = new General();
249 
250 	if (Settings::gameType() == Settings::EnumGameType::Snake) {
251 		m_generalConfigDialog->namePlayer1Label->setText(i18n("Player Name:"));
252 		m_generalConfigDialog->namePlayer2Label->setText(i18n("Opponent:"));
253 	}
254 	else {
255 		m_generalConfigDialog->namePlayer1Label->setText(i18n("Right player:"));
256 		m_generalConfigDialog->namePlayer2Label->setText(i18n("Left player:"));
257 	}
258 
259 	KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral( "settings" ), Settings::self());
260 	dialog->addPage(m_generalConfigDialog, i18n("General"), QStringLiteral( "games-config-options" ));
261 	dialog->addPage(new KGameThemeSelector(dialog, Settings::self(), KGameThemeSelector::NewStuffEnableDownload), i18n("Theme"), QStringLiteral( "games-config-theme" ));
262 	connect(dialog, &KConfigDialog::settingsChanged, this, &KTron::loadSettings);
263 	connect(dialog, &KConfigDialog::settingsChanged, m_tron, &Tron::loadSettings);
264 	dialog->show();
265 }
266 
267 /**
268  * Show highscores
269  */
showHighscores()270 void KTron::showHighscores() {
271 	KScoreDialog scoreDialog(KScoreDialog::Score | KScoreDialog::Name, this);
272 	scoreDialog.initFromDifficulty(Kg::difficulty());
273 	scoreDialog.exec();
274 }
275 
276 /**
277  * Close KTron
278  */
close()279 void KTron::close() {
280 	Settings::self()->save();
281 }
282 
closeEvent(QCloseEvent * event)283 void KTron::closeEvent(QCloseEvent *event)
284 {
285     close();
286     event->accept();
287 }
288 
optionsConfigureKeys()289 void KTron::optionsConfigureKeys()
290 {
291     KShortcutsDialog::showDialog(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
292 }
293 
294 // Key events
keyPressEvent(QKeyEvent * e)295 void KTron::keyPressEvent(QKeyEvent *e)
296 {
297 	if (m_player0Accelerate->shortcuts().contains(e->key()))
298 	{
299 		triggerKey0Accelerate(true);
300 	}
301 	else if (m_player1Accelerate->shortcuts().contains(e->key()))
302 	{
303 		triggerKey1Accelerate(true);
304 	}
305 }
306 
keyReleaseEvent(QKeyEvent * e)307 void KTron::keyReleaseEvent(QKeyEvent *e)
308 {
309 	if (m_player0Accelerate->shortcuts().contains(e->key()))
310 	{
311 		triggerKey0Accelerate(false);
312 	}
313 	else if (m_player1Accelerate->shortcuts().contains(e->key()))
314 	{
315 		triggerKey1Accelerate(false);
316 	}
317 }
318 
319 // Triggers
triggerKey0Up(bool b)320 void KTron::triggerKey0Up(bool b)
321 {
322 	m_tron->triggerKey(0, KBAction::UP, b);
323 }
324 
triggerKey0Down(bool b)325 void KTron::triggerKey0Down(bool b)
326 {
327 	m_tron->triggerKey(0, KBAction::DOWN, b);
328 }
329 
triggerKey0Left(bool b)330 void KTron::triggerKey0Left(bool b)
331 {
332 	m_tron->triggerKey(0, KBAction::LEFT, b);
333 }
334 
triggerKey0Right(bool b)335 void KTron::triggerKey0Right(bool b)
336 {
337 	m_tron->triggerKey(0, KBAction::RIGHT, b);
338 }
339 
triggerKey0Accelerate(bool b)340 void KTron::triggerKey0Accelerate(bool b)
341 {
342 	m_tron->triggerKey(0, KBAction::ACCELERATE, b);
343 }
344 
triggerKey1Up(bool b)345 void KTron::triggerKey1Up(bool b)
346 {
347 	m_tron->triggerKey(1, KBAction::UP, b);
348 }
349 
triggerKey1Down(bool b)350 void KTron::triggerKey1Down(bool b)
351 {
352 	m_tron->triggerKey(1, KBAction::DOWN, b);
353 }
354 
triggerKey1Left(bool b)355 void KTron::triggerKey1Left(bool b)
356 {
357 	m_tron->triggerKey(1, KBAction::LEFT, b);
358 }
359 
triggerKey1Right(bool b)360 void KTron::triggerKey1Right(bool b)
361 {
362 	m_tron->triggerKey(1, KBAction::RIGHT, b);
363 }
364 
triggerKey1Accelerate(bool b)365 void KTron::triggerKey1Accelerate(bool b)
366 {
367 	m_tron->triggerKey(1, KBAction::ACCELERATE, b);
368 }
369 
370 
371 
372 
373