1
/* Yo Emacs, this is -*- C++ -*-
2
*******************************************************************
3
*******************************************************************
9
*******************************************************************
11
* A japanese game similar to mahjongg
13
*******************************************************************
15
* Copyright (C) 1997 by Mario Weilguni <mweilguni@sime.com>
16
* Copyright (C) 2002-2004 Dave Corrie <kde@davecorrie.com>
17
* Copyright (C) 2007 Mauricio Piacentini <mauricio@tabuleiro.com>
19
*******************************************************************
21
* This file is part of the KDE project "KSHISEN"
23
* KSHISEN is free software; you can redistribute it and/or modify
24
* it under the terms of the GNU General Public License as published by
25
* the Free Software Foundation; either version 2, or (at your option)
28
* KSHISEN is distributed in the hope that it will be useful,
29
* but WITHOUT ANY WARRANTY; without even the implied warranty of
30
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
* GNU General Public License for more details.
33
* You should have received a copy of the GNU General Public License
34
* along with KSHISEN; see the file COPYING. If not, write to
35
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
36
* Boston, MA 02110-1301, USA.
38
*******************************************************************
43
#include <kactioncollection.h>
44
#include <kstandardaction.h>
45
#include <kseparator.h>
46
#include <kmessagebox.h>
49
#include <kstandardgameaction.h>
50
#include <khighscore.h>
52
#include <kshortcutsdialog.h>
54
#include <kstatusbar.h>
56
#include <kpushbutton.h>
57
#include <KStandardGuiItem>
58
#include <kconfigdialog.h>
66
#include "ui_settings.h"
68
#include <kmahjonggconfigdialog.h>
70
class Settings : public QWidget, public Ui::Settings
73
Settings(QWidget *parent)
80
App::App(QWidget *parent) : KXmlGuiWindow(parent),
83
highscoreTable = new KHighscore(this);
86
// Would it make sense long term to have a kconfig update rather then
87
// havin both formats supported in the code?
88
if(highscoreTable->hasTable())
96
board = new Board(this);
97
board->setObjectName( "board" );
99
setCentralWidget(board);
103
connect(board, SIGNAL(changed()), this, SLOT(enableItems()));
104
connect(board, SIGNAL(tilesDontMatch()), this, SLOT(notifyTilesDontMatch()));
105
connect(board, SIGNAL(invalidMove()), this, SLOT(notifyInvalidMove()));
106
connect(board, SIGNAL(selectATile()), this, SLOT(notifySelectATile()));
107
connect(board, SIGNAL(selectAMatchingTile()), this, SLOT(notifySelectAMatchingTile()));
108
connect(board, SIGNAL(selectAMove()), this, SLOT(notifySelectAMove()));
110
QTimer *t = new QTimer(this);
112
connect(t, SIGNAL(timeout()), this, SLOT(updateScore()));
113
connect(board, SIGNAL(endOfGame()), this, SLOT(slotEndOfGame()));
115
qApp->processEvents();
121
void App::setupStatusBar()
123
gameTipLabel= new QLabel(i18n("Select a tile"), statusBar());
124
statusBar()->addWidget(gameTipLabel, 1);
126
gameTimerLabel = new QLabel(i18n("Time: 0:00:00"), statusBar());
127
statusBar()->addWidget(gameTimerLabel);
129
gameTilesLabel = new QLabel(i18n("Removed: 0/0"), statusBar());
130
statusBar()->addWidget(gameTilesLabel);
132
gameCheatLabel = new QLabel(i18n("Cheat mode"), statusBar());
133
statusBar()->addWidget(gameCheatLabel);
134
gameCheatLabel->hide();
137
void App::setupActions()
140
KStandardGameAction::gameNew(this, SLOT(newGame()), actionCollection());
141
KStandardGameAction::restart(this, SLOT(restartGame()), actionCollection());
142
KStandardGameAction::pause(this, SLOT(pause()), actionCollection());
143
KStandardGameAction::highscores(this, SLOT(hallOfFame()), actionCollection());
144
KStandardGameAction::quit(this, SLOT(close()), actionCollection());
147
KStandardGameAction::undo(this, SLOT(undo()), actionCollection());
148
KStandardGameAction::redo(this, SLOT(redo()), actionCollection());
149
KStandardGameAction::hint(this, SLOT(hint()), actionCollection());
150
//new KAction(i18n("Is Game Solvable?"), 0, this,
151
// SLOT(isSolvable()), actionCollection(), "move_solvable");
155
//(void)new KAction(i18n("&Finish"), 0, board, SLOT(finish()), actionCollection(), "move_finish");
159
KStandardAction::preferences(this, SLOT(showSettings()), actionCollection());
162
void App::hallOfFame()
174
void App::restartGame()
176
board->setUpdatesEnabled(false);
177
while(board->canUndo())
179
board->setUpdatesEnabled(true);
187
void App::isSolvable()
189
if(board->solvable())
190
KMessageBox::information(this, i18n("This game is solvable."));
192
KMessageBox::information(this, i18n("This game is NOT solvable."));
197
lockMenus(board->pause());
220
board->makeHintMove();
228
void App::lockMenus(bool lock)
230
// Disable all actions apart from (un)pause, quit and those that are help-related.
231
// (Only undo/redo and hint actually *need* to be disabled, but disabling everything
232
// provides a good visual hint to the user, that they need to unpause to continue.
233
KMenu* help = findChild<KMenu*>("help" );
234
QList<QAction*> actions = actionCollection()->actions();
235
QList<QAction*>::const_iterator actionIter = actions.constBegin();
236
QList<QAction*>::const_iterator actionIterEnd = actions.constEnd();
238
while(actionIter != actionIterEnd)
240
QAction* a = *actionIter;
241
if(!a->associatedWidgets().contains(help))
242
a->setEnabled(!lock);
246
actionCollection()->action(KStandardGameAction::name(KStandardGameAction::Pause))->setEnabled(true);
247
actionCollection()->action(KStandardGameAction::name(KStandardGameAction::Quit))->setEnabled(true);
252
void App::enableItems()
254
if(!board->isPaused())
256
actionCollection()->action(KStandardGameAction::name(KStandardGameAction::Undo))->setEnabled(board->canUndo());
257
actionCollection()->action(KStandardGameAction::name(KStandardGameAction::Redo))->setEnabled(board->canRedo());
258
actionCollection()->action(KStandardGameAction::name(KStandardGameAction::Restart))->setEnabled(board->canUndo());
262
void App::slotEndOfGame()
264
if(board->tilesLeft() > 0)
266
KMessageBox::information(this, i18n("No more moves possible!"), i18n("End of Game"));
270
// create highscore entry
272
hs.seconds = board->getTimeForGame();
273
hs.x = board->x_tiles();
274
hs.y = board->y_tiles();
275
hs.gravity = (int)board->gravityFlag();
277
// check if we made it into Top10
278
bool isHighscore = false;
279
if(highscore.size() < HIGHSCORE_MAX)
281
else if(isBetter(hs, highscore[HIGHSCORE_MAX-1]))
284
if(isHighscore && !cheat)
286
hs.name = getPlayerName();
287
hs.date = time((time_t*)0);
288
int rank = insertHighscore(hs);
293
QString s = i18n("Congratulations! You made it in %1:%2:%3",
294
QString().sprintf("%02d", board->getTimeForGame()/3600),
295
QString().sprintf("%02d", (board->getTimeForGame() / 60) % 60),
296
QString().sprintf("%02d", board->getTimeForGame() % 60));
298
if(isHighscore) // player would have been in the hisghscores if he did not cheat
300
s += '\n' + i18n("You could have been in the highscores if you did not use Undo or Hint. Try without them next time.");
303
KMessageBox::information(this, s, i18n("End of Game"));
311
void App::notifySelectATile()
313
gameTipLabel->setText(i18n("Select a tile"));
316
void App::notifySelectAMatchingTile()
318
gameTipLabel->setText(i18n("Select a matching tile"));
321
void App::notifySelectAMove()
323
gameTipLabel->setText(i18n("Select the move you want by clicking on the blue line"));
326
void App::notifyTilesDontMatch()
328
gameTipLabel->setText(i18n("This tile did not match the one you selected"));
331
void App::notifyInvalidMove()
333
gameTipLabel->setText(i18n("You cannot make this move"));
336
void App::updateScore()
338
int t = board->getTimeForGame();
339
QString s = i18n(" Your time: %1:%2:%3 %4",
340
QString().sprintf("%02d", t / 3600 ),
341
QString().sprintf("%02d", (t / 60) % 60 ),
342
QString().sprintf("%02d", t % 60 ),
343
board->isPaused()?i18n("(Paused) "):QString());
345
//statusBar()->changeItem(s, SBI_TIME);
346
gameTimerLabel->setText(s);
349
int tl = (board->x_tiles() * board->y_tiles());
350
s = i18n(" Removed: %1/%2 ",
351
QString().sprintf("%d", tl - board->tilesLeft()),
352
QString().sprintf("%d", tl ));
354
//statusBar()->changeItem(s, SBI_TILES);
355
gameTilesLabel->setText(s);
358
void App::setCheatMode()
360
// set the cheat mode if not set
364
gameCheatLabel->show();
368
void App::resetCheatMode()
370
// reset cheat mode if set
374
gameCheatLabel->hide();
378
QString App::getPlayerName()
381
dlg.setObjectName( "Hall of Fame" );
382
dlg.setButtons( KDialog::Ok );
384
QWidget* dummy = new QWidget( &dlg );
385
dlg.setMainWidget( dummy );
387
QLabel *l1 = new QLabel(i18n("You have made it into the \"Hall Of Fame\". Type in\nyour name so mankind will always remember\nyour cool rating."), dummy);
389
QLabel *l2 = new QLabel(i18n("Your name:"), dummy);
391
KLineEdit *e = new KLineEdit( dummy );
392
e->setMinimumWidth( e->fontMetrics().width( "XXXXXXXXXXXXXXXX" ) );
393
e->setText( lastPlayerName );
397
QHBoxLayout *tl1 = new QHBoxLayout;
400
QVBoxLayout *tl = new QVBoxLayout( dummy );
408
lastPlayerName = e->text();
410
if(lastPlayerName.isEmpty())
412
return lastPlayerName;
415
int App::getScore(const HighScore &hs)
417
double ntiles = hs.x*hs.y;
418
double tilespersec = ntiles/(double)hs.seconds;
420
double sizebonus = std::sqrt(ntiles/(double)(14.0 * 6.0));
421
double points = tilespersec / 0.14 * 100.0;
424
return (int)(2.0 * points * sizebonus);
426
return (int)(points * sizebonus);
429
bool App::isBetter(const HighScore &hs, const HighScore &than)
431
if(getScore(hs) > getScore(than))
437
int App::insertHighscore(const HighScore &hs)
441
if(highscore.size() == 0)
450
HighScore last = highscore[highscore.size() - 1];
451
if(isBetter(hs, last) || (highscore.size() < HIGHSCORE_MAX))
453
if(highscore.size() == HIGHSCORE_MAX)
455
highscore[HIGHSCORE_MAX - 1] = hs;
459
highscore.resize(highscore.size()+1);
460
highscore[highscore.size() - 1] = hs;
464
int bestsofar = highscore.size() - 1;
465
for(i = highscore.size() - 1; i > 0; i--)
467
if(isBetter(highscore[i], highscore[i-1]))
470
HighScore temp = highscore[i-1];
471
highscore[i-1] = highscore[i];
484
void App::readHighscore()
486
QStringList hi_x, hi_y, hi_sec, hi_date, hi_grav, hi_name;
487
hi_x = highscoreTable->readList("x", HIGHSCORE_MAX);
488
hi_y = highscoreTable->readList("y", HIGHSCORE_MAX);
489
hi_sec = highscoreTable->readList("seconds", HIGHSCORE_MAX);
490
hi_date = highscoreTable->readList("date", HIGHSCORE_MAX);
491
hi_grav = highscoreTable->readList("gravity", HIGHSCORE_MAX);
492
hi_name = highscoreTable->readList("name", HIGHSCORE_MAX);
496
for (int i = 0; i < hi_x.count(); i++)
498
highscore.resize(i+1);
502
hs.x = hi_x[i].toInt();
503
hs.y = hi_y[i].toInt();
504
hs.seconds = hi_sec[i].toInt();
505
hs.date = hi_date[i].toInt();
506
hs.date = hi_date[i].toInt();
507
hs.gravity = hi_grav[i].toInt();
508
hs.name = hi_name[i];
514
void App::readOldHighscore()
516
// this is for before-KHighscore-highscores
519
KSharedConfig::Ptr conf = KGlobal::config();
524
KConfigGroup group = conf->group("Hall of Fame");
525
while ((i < (int)HIGHSCORE_MAX) && !eol)
527
s.sprintf("Highscore_%d", i);
530
e = group.readEntry(s,QString());
531
highscore.resize(i+1);
535
QStringList e = group.readEntry(s, QString()).split(' ');
536
int nelem = e.count();
537
hs.x = e.at(0).toInt();
538
hs.y = e.at(1).toInt();
539
hs.seconds = e.at(2).toInt();
540
hs.date = e.at(3).toInt();
542
if(nelem == 4) // old version <= 1.1
549
hs.gravity = e.at(4).toInt();
562
// // freshly installed, add my own highscore
563
// if(highscore.size() == 0)
569
// hs.name = "Mario";
570
// highscore.resize(1);
571
// highscore[0] = hs;
574
// write in new KHighscore format
576
// read form KHighscore format
580
void App::writeHighscore()
583
QStringList hi_x, hi_y, hi_sec, hi_date, hi_grav, hi_name;
584
for(i = 0; i < (int)highscore.size(); i++)
586
HighScore hs = highscore[i];
587
hi_x.append(QString::number(hs.x));
588
hi_y.append(QString::number(hs.y));
589
hi_sec.append(QString::number(hs.seconds));
590
hi_date.append(QString::number(hs.date));
591
hi_grav.append(QString::number(hs.gravity));
592
hi_name.append(hs.name);
594
highscoreTable->writeList("x", hi_x);
595
highscoreTable->writeList("y", hi_y);
596
highscoreTable->writeList("seconds", hi_sec);
597
highscoreTable->writeList("date", hi_date);
598
highscoreTable->writeList("gravity", hi_grav);
599
highscoreTable->writeList("name", hi_name);
600
highscoreTable->writeAndUnlock();
603
void App::showHighscore(int focusitem)
605
// this may look a little bit confusing...
607
dlg.setObjectName( "hall_Of_fame" );
608
dlg.setButtons( KDialog::Close );
609
dlg.setWindowTitle(i18n("Hall of Fame"));
611
QWidget* dummy = new QWidget(&dlg);
612
dlg.setMainWidget(dummy);
614
QVBoxLayout *tl = new QVBoxLayout(dummy);
617
QLabel *l = new QLabel(i18n("Hall of Fame"), dummy);
622
l->setAlignment(Qt::AlignCenter);
625
// insert highscores in a gridlayout
626
QGridLayout *table = new QGridLayout;
628
tl->addLayout(table, 1);
630
// add a separator line
631
KSeparator *sep = new KSeparator(dummy);
632
table->addWidget(sep, 1, 0, 1, 5);
637
l = new QLabel(i18n("Rank"), dummy);
639
table->addWidget(l, 0, 0);
640
l = new QLabel(i18n("Name"), dummy);
642
table->addWidget(l, 0, 1);
643
l = new QLabel(i18n("Time"), dummy);
645
table->addWidget(l, 0, 2);
646
l = new QLabel(i18n("Size"), dummy);
648
table->addWidget(l, 0, 3);
649
l = new QLabel(i18n("Score"), dummy);
651
table->addWidget(l, 0, 4);
657
for(i = 0; i < 10; i++)
660
if(i < highscore.size())
664
s.sprintf("%d", i+1);
665
e[i][0] = new QLabel(s, dummy);
668
if(i < highscore.size())
669
e[i][1] = new QLabel(hs.name, dummy);
671
e[i][1] = new QLabel("", dummy);
675
if(i < highscore.size())
677
ti = ti.addSecs(hs.seconds);
678
s.sprintf("%02d:%02d:%02d", ti.hour(), ti.minute(), ti.second());
679
e[i][2] = new QLabel(s, dummy);
683
e[i][2] = new QLabel("", dummy);
687
if(i < highscore.size())
688
s.sprintf("%d x %d", hs.x, hs.y);
692
e[i][3] = new QLabel(s, dummy);
695
if(i < highscore.size())
699
.arg(hs.gravity ? i18n("(gravity)") : QString(""));
706
e[i][4] = new QLabel(s, dummy);
707
e[i][4]->setAlignment(Qt::AlignRight);
713
for(i = 0; i < 10; i++)
715
for(j = 0; j < 5; j++)
717
if((int)i == focusitem)
720
table->addWidget(e[i][j], i+2, j, Qt::AlignCenter);
727
void App::keyBindings()
729
KShortcutsDialog::configure( actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this );
734
* Show Settings dialog.
736
void App::showSettings(){
737
if(KConfigDialog::showDialog("settings"))
740
//Use the classes exposed by LibKmahjongg for our configuration dialog
741
KMahjonggConfigDialog *dialog = new KMahjonggConfigDialog(this, "settings", Prefs::self());
742
dialog->addPage(new Settings(0), i18n("General"), "games-config-options");
743
dialog->addTilesetPage();
744
dialog->addBackgroundPage();
745
dialog->setHelp(QString(),"kshisen");
746
connect(dialog, SIGNAL(settingsChanged(const QString &)), board, SLOT(loadSettings()));