summaryrefslogtreecommitdiffstats
path: root/kpacman/score.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kpacman/score.cpp')
-rw-r--r--kpacman/score.cpp627
1 files changed, 627 insertions, 0 deletions
diff --git a/kpacman/score.cpp b/kpacman/score.cpp
new file mode 100644
index 0000000..1bbd427
--- /dev/null
+++ b/kpacman/score.cpp
@@ -0,0 +1,627 @@
+#include <score.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <kaccel.h>
+
+#include <kapp.h>
+#include <kconfig.h>
+#include <kstddirs.h>
+#include <kmessagebox.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qdstream.h>
+#include <qkeycode.h>
+#include <qtimer.h>
+#include <qfileinfo.h>
+
+#include "bitfont.h"
+
+Score::Score(QWidget *parent, const char *name, int Scheme, int Mode, Bitfont *font) : QWidget(parent, name)
+{
+ setFocusPolicy(QWidget::StrongFocus);
+
+ paused = FALSE;
+
+ lastScore = -1;
+ lastPlayer = -1;
+
+ cursorBlinkTimer = 0;
+ cursorBlinkMS = -1;
+ cursor.x = -1;
+ cursor.y = -1;
+ cursor.on = FALSE;
+ cursor.chr = QChar('?');
+
+ initKeys();
+
+ scheme = Scheme;
+ mode = Mode;
+ confScheme();
+
+ bitfont = font;
+
+ highscoreFile.setName(locateHighscoreFilePath().filePath());
+ read();
+
+ for (int p = 0; p < maxPlayer; p++) {
+ playerScore[p] = 0;
+ playerName[p] = getenv("LOGNAME");
+ if (playerName[p].length() < minPlayerNameLength)
+ playerName[p].setExpand(minPlayerNameLength-1, ' ');
+
+ for (uint i = 0; i < playerName[p].length(); i++)
+ if (playerName[p].at(i) < bitfont->firstChar() ||
+ playerName[p].at(i) > bitfont->lastChar())
+ playerName[p].at(i) = playerName[p].at(i).upper();
+ }
+}
+
+Score::~Score()
+{
+ // write();
+}
+
+void Score::paintEvent( QPaintEvent *e)
+{
+ if (rect(1, 0, i18n(" 1UP ")).intersects(e->rect())) {
+ QPixmap pix;
+ QColor fg = BLACK;
+ if (cursor.on || paused || lastPlayer != 0)
+ fg = WHITE;
+ pix = bitfont->text(i18n(" 1UP "), fg, BLACK);
+ bitBlt(this, x(1), y(0), &pix);
+ }
+
+ if (rect(8, 0, i18n(" HIGH SCORE ")).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(i18n(" HIGH SCORE "), WHITE, BLACK);
+ bitBlt(this, x(8), y(0), &pix);
+ }
+
+ if (maxPlayer > 1 && rect(21, 0, i18n(" 2UP ")).intersects(e->rect())) {
+ QPixmap pix;
+ QColor fg = BLACK;
+ if (cursor.on || paused || lastPlayer != 1)
+ fg = WHITE;
+ pix = bitfont->text(i18n(" 2UP "), fg, BLACK);
+ bitBlt(this, x(21), y(0), &pix);
+ }
+
+ QString s;
+
+ s.sprintf("%6d0", playerScore[0]/10);
+ if (rect(0, 1, s).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(s, WHITE, BLACK);
+ bitBlt(this, x(0), y(1), &pix);
+ }
+
+ s.sprintf("%8d0", HighScore/10);
+ if (rect(8, 1, s).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(s, WHITE, BLACK);
+ bitBlt(this, x(8), y(1), &pix);
+ }
+
+ if (lastScore >= 0) {
+ if (rect(1, 4*1.25, i18n(" CONGRATULATIONS ")).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(i18n(" CONGRATULATIONS "), YELLOW, BLACK);
+ bitBlt(this, x(1), y(4*1.25), &pix);
+ }
+ if (rect(1, 6*1.25, i18n(" YOU HAVE ARCHIEVED ")).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(i18n(" YOU HAVE ARCHIEVED "), CYAN, BLACK);
+ bitBlt(this, x(1), y(6*1.25), &pix);
+ }
+ if (rect(1, 7*1.25, i18n(" A SCORE IN THE TOP 10. ")).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(i18n(" A SCORE IN THE TOP 10. "), CYAN, BLACK);
+ bitBlt(this, x(1), y(7*1.25), &pix);
+ }
+ if (rect(1, 8*1.25, i18n(" ")).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(i18n(" "), CYAN, BLACK);
+ bitBlt(this, x(1), y(8*1.25), &pix);
+ }
+ }
+
+ if (rect(1, 9.5*1.25, i18n("RNK SCORE NAME DATE")).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(i18n("RNK SCORE NAME DATE"), WHITE, BLACK);
+ bitBlt(this, x(1), y(9.5*1.25), &pix);
+ }
+
+ for (int i = 0; i < 10; i++) {
+ s.sprintf("%2d%9d %-3.3s %-8.8s",
+ i+1, hallOfFame[i].points, hallOfFame[i].name.utf8().data(),
+ formatDate(hallOfFame[i].moment.date()).data());
+ if (rect(1, (11+i)*1.25, s).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(s, (i == lastScore) ? YELLOW : WHITE, BLACK);
+ bitBlt(this, x(1), y((11+i)*1.25), &pix);
+ }
+ }
+
+ if (cursor.x != -1 && cursor.y != -1 && cursor.on) {
+ if (rect(cursor.x, (cursor.y*1.25), cursor.chr).intersects(e->rect())) {
+ QPixmap pix = bitfont->text(cursor.chr, BLACK, YELLOW);
+ bitBlt(this, x(cursor.x), y(cursor.y*1.25), &pix);
+ }
+ }
+
+ if (paused) {
+
+ QPixmap pix = bitfont->text(i18n("PAUSED"), RED, BLACK);
+ QRect r = bitfont->rect(i18n("PAUSED"));
+ r.moveCenter(QPoint(this->width()/2, this->height()/2));
+
+ bitBlt(this, r.x(), r.y(), &pix);
+ }
+}
+
+void Score::timerEvent(QTimerEvent *e)
+{
+ cursor.on = !cursor.on;
+
+ if (paused)
+ return;
+
+ if (cursor.x != -1 && cursor.y != -1)
+ repaint(rect(cursor.x, cursor.y*1.25, cursor.chr), FALSE);
+ scrollRepeat = FALSE;
+
+ if (lastPlayer == 0)
+ repaint(rect(1, 0, i18n(" 1UP ")), FALSE);
+
+ if (lastPlayer == 1)
+ repaint(rect(21, 0, i18n(" 2UP ")), FALSE);
+}
+
+void Score::keyPressEvent(QKeyEvent *k)
+{
+ if (lastScore < 0 || lastPlayer < 0 || lastPlayer >= maxPlayer || paused) {
+ k->ignore();
+ return;
+ }
+
+ int x = cursor.x;
+ int y = cursor.y;
+
+ uint key = k->key();
+
+ if (scrollRepeat && (key == UpKey || key == Key_Up || key == DownKey || key == Key_Down)) {
+ k->ignore();
+ return;
+ }
+
+ if (key != Key_Return) {
+ if (key == RightKey || key == Key_Right)
+ if (++cursor.x > 16)
+ cursor.x = 14;
+ if (key == LeftKey || key == Key_Left)
+ if (--cursor.x < 14)
+ cursor.x = 16;
+ if (key == UpKey || key == Key_Up)
+ if (cursor.chr.unicode() < bitfont->lastChar())
+ cursor.chr = cursor.chr.unicode()+1;
+ else
+ cursor.chr = bitfont->firstChar();
+ if (key == DownKey || key == Key_Down)
+ if (cursor.chr.unicode() > bitfont->firstChar())
+ cursor.chr = cursor.chr.unicode()-1;
+ else
+ cursor.chr = bitfont->lastChar();
+
+ if (cursor.x == x && cursor.y == y &&
+ cursor.chr == hallOfFame[lastScore].name.at(cursor.x-14)) {
+ uint ascii = k->ascii();
+
+ if (ascii < bitfont->firstChar() || ascii > bitfont->lastChar())
+ ascii = toupper(ascii);
+
+ if (ascii >= bitfont->firstChar() && ascii <= bitfont->lastChar()) {
+ cursor.chr = ascii;
+ hallOfFame[lastScore].name.at(cursor.x-14) = cursor.chr;
+ if (++cursor.x > 16)
+ cursor.x = 14;
+ }
+ }
+ }
+
+ if (key == Key_Return) {
+ playerName[lastPlayer] = hallOfFame[lastScore].name;
+ write();
+ read();
+ lastScore = -1;
+ lastPlayer = -1;
+ cursor.x = -1;
+ cursor.y = -1;
+ emit gameFinished();
+ end();
+ }
+
+ if (x != cursor.x || y != cursor.y) {
+ if (cursor.x != -1)
+ cursor.chr = hallOfFame[lastScore].name.at(cursor.x-14);
+ scrollRepeat = FALSE;
+ repaint(rect(x, y*1.25, cursor.chr), FALSE);
+ } else
+ hallOfFame[lastScore].name.at(cursor.x-14) = cursor.chr;
+
+ if (key == UpKey || key == Key_Up || key == DownKey || key == Key_Down)
+ scrollRepeat = TRUE;
+ else
+ repaint(rect(cursor.x, cursor.y*1.25, cursor.chr), FALSE);
+}
+
+void Score::initKeys()
+{
+ QString up("Up");
+ up = kapp->config()->readEntry("upKey", (const char*) up);
+ UpKey = KAccel::stringToKey(up);
+
+ QString down("Down");
+ down = kapp->config()->readEntry("downKey", (const char*) down);
+ DownKey = KAccel::stringToKey(down);
+
+ QString left("Left");
+ left = kapp->config()->readEntry("leftKey", (const char*) left);
+ LeftKey = KAccel::stringToKey(left);
+
+ QString right("Right");
+ right = kapp->config()->readEntry("rightKey", (const char*) right);
+ RightKey = KAccel::stringToKey(right);
+}
+
+void Score::confTiming(bool defGroup)
+{
+ if (defGroup || kapp->config()->hasKey("CursorBlinkMS"))
+ cursorBlinkMS = kapp->config()->readNumEntry("CursorBlinkMS", 250);
+ if (defGroup || kapp->config()->hasKey("HallOfFameMS"))
+ hallOfFameMS = kapp->config()->readNumEntry("HallOfFameMS", 7000);
+ if (defGroup || kapp->config()->hasKey("AfterPauseMS"))
+ afterPauseMS = kapp->config()->readNumEntry("AfterPauseMS", 1000);
+}
+
+void Score::confScheme()
+{
+ QString oldgroup = kapp->config()->group();
+ QString newgroup;
+
+ // if not set, read mode and scheme from the configfile
+ if (mode == -1 && scheme == -1) {
+ scheme = kapp->config()->readNumEntry("Scheme", -1);
+ mode = kapp->config()->readNumEntry("Mode", -1);
+
+ // if mode is not set in the defGroup-group, lookup the scheme group
+ if (scheme != -1 || mode == -1) {
+ newgroup.sprintf("Scheme %d", scheme);
+ kapp->config()->setGroup(newgroup);
+
+ mode = kapp->config()->readNumEntry("Mode", -1);
+ kapp->config()->setGroup(oldgroup);
+ }
+ }
+
+ int oldCursorBlinkMS = cursorBlinkMS;
+
+ confTiming();
+
+ if (mode != -1) {
+ newgroup.sprintf("Mode %d", mode);
+ kapp->config()->setGroup(newgroup);
+
+ confTiming(FALSE);
+ }
+
+ if (scheme != -1) {
+ newgroup.sprintf("Scheme %d", scheme);
+ kapp->config()->setGroup(newgroup);
+
+ confTiming(FALSE);
+ }
+
+ if (cursorBlinkMS != oldCursorBlinkMS) {
+ if (cursorBlinkTimer)
+ killTimer(cursorBlinkTimer);
+ cursorBlinkTimer = startTimer(cursorBlinkMS);
+ }
+
+ kapp->config()->setGroup(oldgroup);
+}
+
+void Score::setScheme(int Scheme, int Mode, Bitfont *font)
+{
+ mode = Mode;
+ scheme = Scheme;
+
+ confScheme();
+
+ bitfont = font;
+
+ for (int p = 0; p < maxPlayer; p++)
+ for (uint i = 0; i < playerName[p].length(); i++)
+ if (playerName[p].at(i) < bitfont->firstChar() ||
+ playerName[p].at(i) > bitfont->lastChar())
+ playerName[p].at(i) = playerName[p].at(i).upper();
+
+ for (int i = 0; i < 10; i++)
+ for (uint j = 0; j < hallOfFame[i].name.length(); j++)
+ if (hallOfFame[i].name.at(j) < bitfont->firstChar() ||
+ hallOfFame[i].name.at(j) > bitfont->lastChar())
+ hallOfFame[i].name.at(j) = hallOfFame[i].name.at(j).upper();
+
+ if (cursor.chr.unicode() < bitfont->firstChar() ||
+ cursor.chr.unicode() > bitfont->lastChar())
+ cursor.chr = cursor.chr.upper();
+}
+
+void Score::set(int score)
+{
+ set(score, 0);
+}
+
+void Score::set(int score, int player)
+{
+ if (player < 0 || player >= maxPlayer)
+ return;
+
+ lastPlayer = player;
+ playerScore[lastPlayer] = score;
+
+ QString s;
+
+ s.sprintf("%6d0", playerScore[lastPlayer]/10);
+ repaint(rect(0, 1, s), FALSE);
+
+ if (score > HighScore) {
+ HighScore = score;
+ s.sprintf("%8d0", HighScore/10);
+ repaint(rect(8, 1, s), FALSE);
+ }
+}
+
+/*
+ * Set the score for player after the game if over. If the score is in the
+ * high scores then the hall of fame is updated (shifted) and the scoreboard
+ * is shown.
+ */
+
+void Score::setScore(int level, int player)
+{
+ // pointer to the array-position of a new archived top score
+ lastScore = -1;
+
+ // was it a real game or just a demo (level == 0)
+ if (player >= 0 && player < maxPlayer && level != 0) {
+
+ lastPlayer = player;
+
+ for (int i = 0; i < 10; i++)
+ if ( playerScore[lastPlayer] > hallOfFame[i].points) {
+ lastScore = i;
+ break;
+ }
+ }
+
+ // no new highscore archived, finish after specified time
+ if (lastScore < 0) {
+ // enable gameNew directly for an immediate next try
+ emit gameFinished();
+ lastPlayer = -1;
+ QTimer::singleShot(hallOfFameMS, this, SLOT(end()));
+ return;
+ }
+
+ // shift old (lower) scores to make place for the new one
+ for (int i = 9; i > lastScore && i > 0; i--)
+ hallOfFame[i] = hallOfFame[i-1];
+
+ hallOfFame[lastScore].points = playerScore[lastPlayer];
+ hallOfFame[lastScore].levels = level;
+ hallOfFame[lastScore].moment = QDateTime::currentDateTime();
+ hallOfFame[lastScore].name = playerName[lastPlayer];
+
+ cursor.x = 14;
+ cursor.y = 11+lastScore;
+ cursor.chr = hallOfFame[lastScore].name.at(cursor.x-14);
+
+// startTimer(cursorBlinkMS);
+ setFocus();
+}
+
+/*
+ * Read the highscores, if no file or a file shorter than 4 bytes (versions before 0.2.4 stores only
+ * the points of one highscore) exists - the highscores were initialized with default values.
+ */
+void Score::read()
+{
+ if (highscoreFile.exists() && highscoreFile.size() > 4) {
+ if (highscoreFile.open(IO_ReadOnly)) {
+ QDataStream s(&highscoreFile);
+ char *name;
+ for (int i = 0; i < 10; i++) {
+ s >> hallOfFame[i].points >> hallOfFame[i].levels >> hallOfFame[i].duration >>
+ hallOfFame[i].moment >> name;
+ hallOfFame[i].name = QString::fromLatin1(name);
+ delete(name);
+ }
+ highscoreFile.close();
+ }
+ } else {
+ for (int i = 0; i < 10; i++) {
+ hallOfFame[i].points = 5000;
+ hallOfFame[i].levels = 0;
+ hallOfFame[i].duration = QTime();
+ hallOfFame[i].moment = QDateTime();
+ hallOfFame[i].name = "???";
+ }
+ // write();
+ }
+
+ for (int i = 0; i < 10; i++)
+ for (uint j = 0; j < hallOfFame[i].name.length(); j++)
+ if (hallOfFame[i].name.at(j) < bitfont->firstChar() ||
+ hallOfFame[i].name.at(j) > bitfont->lastChar())
+ hallOfFame[i].name.at(j) = hallOfFame[i].name.at(j).upper();
+
+ HighScore = hallOfFame[0].points;
+}
+
+void Score::write()
+{
+ if (!highscoreFile.exists() && highscoreFile.name() == systemHighscoreFileInfo.filePath())
+ KMessageBox::information(0,
+ i18n("You're going to create the highscore-file\n"
+ "'%1'\n"
+ "for your maschine, that should be used systemwide.\n"
+ "\n"
+ "To grant access to the other users, set the appropriate rights (a+w)\n"
+ "on that file or ask your systemadministator for that favor.\n"
+ "\n"
+ "To use a different directory or filename for the highscores,"
+ "specify them in the configfile (kpacmanrc:highscoreFilePath)."
+ ).arg(systemHighscoreFileInfo.filePath()));
+
+ if (highscoreFile.name() == privateHighscoreFileInfo.filePath())
+ KMessageBox::information(0,
+ i18n("You're using a private highscore-file, that's mostly because of\n"
+ "missing write-access to the systemwide file\n"
+ "'%1' .\n"
+ "\n"
+ "Ask your systemadministrator for granting you access to that file,\n"
+ "by setting the appropriate rights (a+w) on it.\n"
+ "\n"
+ "To use a different directory or filename for the highscores,"
+ "specify them in the configfile (kpacmanrc:highscoreFilePath)."
+ ).arg(systemHighscoreFileInfo.filePath()),
+ QString::null, "PrivateHighscore");
+
+ if (highscoreFile.open(IO_WriteOnly)) {
+ QDataStream s(&highscoreFile);
+ for (int i = 0; i < 10; i++)
+ s << hallOfFame[i].points << hallOfFame[i].levels << hallOfFame[i].duration <<
+ hallOfFame[i].moment << hallOfFame[i].name.latin1();
+ highscoreFile.close();
+ }
+}
+
+void Score::setPause(bool Paused)
+{
+ paused = Paused;
+
+ QRect r = bitfont->rect(i18n("PAUSED"));
+ r.moveCenter(QPoint(this->width()/2, this->height()/2));
+ repaint(r, TRUE);
+
+ // repaint 1UP or 2UP
+ repaint(FALSE);
+}
+
+void Score::end()
+{
+ if (paused) {
+ QTimer::singleShot(afterPauseMS, this, SLOT(end()));
+ return;
+ }
+
+ // repaint 1UP or 2UP
+ repaint(FALSE);
+
+ // if lastPlayer != -1 we're already in playing mode
+ if (lastPlayer == -1)
+ emit gameHighscores();
+}
+
+/*
+ * Return the date in a formatted QString. The format can be changed using internationalization
+ * of the string "YY/MM/DD". Invalid QDate's where returned as "00/00/00".
+ */
+QString Score::formatDate(QDate date)
+{
+ QString s = i18n("@YY@/@MM@/@DD@");
+
+ QString dd;
+ dd.sprintf("%02d", date.isValid() ? date.year() % 100 : 0);
+ s.replace(QRegExp("@YY@"), dd);
+ dd.sprintf("%02d", date.isValid() ? date.month() : 0);
+ s.replace(QRegExp("@MM@"), dd);
+ dd.sprintf("%02d", date.isValid() ? date.day() : 0);
+ s.replace(QRegExp("@DD@"), dd);
+
+ return s;
+}
+
+QRect Score::rect(int col, float row, QString str, int align)
+{
+ QRect r = bitfont->rect(str);
+ r.moveBy(x(col), y(row));
+
+ int dx = 0;
+ int dy = 0;
+
+ if (align & AlignLeft || align & AlignRight) {
+ dx = (str.length()-1) * (bitfont->width()/2);
+ if (align & AlignRight)
+ dx *= -1;
+ }
+
+ if (align & AlignTop || align & AlignBottom) {
+ dy = bitfont->height()/2;
+ if (align & AlignBottom)
+ dy *= -1;
+ }
+
+ if (dx != 0 || dy != 0)
+ r.moveBy(dx, dy);
+
+ return r;
+}
+
+int Score::x(int col)
+{
+ return col*bitfont->width();
+}
+
+int Score::y(float row)
+{
+ return (int) (row*(bitfont->height()+bitfont->height()/4));
+}
+
+/**
+ * Ermittelt die zu benutzende "highscore"-Datei, in die auch geschrieben werden kann.
+ * Über den "highscoreFilePath"-KConfig-Eintrag, kann abweichend von der Standardlokation
+ * der Standort der "highscore"-Datei spezifiziert werden.
+ * Wenn die systemweite "highscore"-Datei nicht beschrieben werden kann, wird mit einer
+ * privaten Datei gearbeitet.
+ */
+QFileInfo Score::locateHighscoreFilePath()
+{
+ QFileInfo systemHighscoreDirPath;
+ QStringList systemHighscoreDirs;
+
+ // Schreibfähige "private" highscore-Datei ermitteln für den fallback.
+ privateHighscoreFileInfo.setFile(KGlobal::dirs()->saveLocation("appdata")+highscoreName);
+
+ // FilePath aus der Konfigurationsdatei benutzen
+ systemHighscoreFileInfo.setFile(kapp->config()->readEntry("HighscoreFilePath"));
+
+ // Kein Wert aus der Konfiguration erhalten, dann die "system"-Datei suchen.
+ if (systemHighscoreFileInfo.filePath().isEmpty())
+ systemHighscoreDirs = KGlobal::dirs()->resourceDirs("appdata");
+ else
+ systemHighscoreDirs = QStringList(systemHighscoreFileInfo.filePath());
+
+ for (QStringList::Iterator i = systemHighscoreDirs.begin(); i != systemHighscoreDirs.end(); ++i) {
+
+ systemHighscoreFileInfo.setFile(*i);
+ if (systemHighscoreFileInfo.fileName().isEmpty())
+ systemHighscoreFileInfo.setFile(systemHighscoreFileInfo.dirPath()+"/"+highscoreName);
+
+ // privateHighscoreFileInfo für die "system" Suche ignorieren
+ if (systemHighscoreFileInfo.filePath() != privateHighscoreFileInfo.filePath())
+ if (!systemHighscoreFileInfo.exists()) {
+ systemHighscoreDirPath.setFile(systemHighscoreFileInfo.dirPath());
+ if (systemHighscoreDirPath.exists() && systemHighscoreDirPath.isWritable())
+ return systemHighscoreFileInfo;
+ } else
+ if (systemHighscoreFileInfo.isWritable())
+ return systemHighscoreFileInfo;
+ }
+
+ return privateHighscoreFileInfo;
+}