#include #include #include #include #include #include #include #include #include #include #include #include #include #include "bitfont.h" #include "score.h" Score::Score(TQWidget *parent, const char *name, int Scheme, int Mode, Bitfont *font) : TQWidget(parent, name) { setFocusPolicy(TQWidget::StrongFocus); paused = FALSE; lastScore = -1; lastPlayer = -1; cursorBlinkTimer = 0; cursorBlinkMS = -1; cursor.x = -1; cursor.y = -1; cursor.on = FALSE; cursor.chr = TQChar('?'); 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( TQPaintEvent *e) { if (rect(1, 0, i18n(" 1UP ")).intersects(e->rect())) { TQPixmap pix; TQColor 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())) { TQPixmap 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())) { TQPixmap pix; TQColor fg = BLACK; if (cursor.on || paused || lastPlayer != 1) fg = WHITE; pix = bitfont->text(i18n(" 2UP "), fg, BLACK); bitBlt(this, x(21), y(0), &pix); } TQString s; s.sprintf("%6d0", playerScore[0]/10); if (rect(0, 1, s).intersects(e->rect())) { TQPixmap 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())) { TQPixmap 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())) { TQPixmap 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())) { TQPixmap 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())) { TQPixmap 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())) { TQPixmap 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())) { TQPixmap 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()).utf8().data()); if (rect(1, (11+i)*1.25, s).intersects(e->rect())) { TQPixmap 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())) { TQPixmap pix = bitfont->text(cursor.chr, BLACK, YELLOW); bitBlt(this, x(cursor.x), y(cursor.y*1.25), &pix); } } if (paused) { TQPixmap pix = bitfont->text(i18n("PAUSED"), RED, BLACK); TQRect r = bitfont->rect(i18n("PAUSED")); r.moveCenter(TQPoint(this->width()/2, this->height()/2)); bitBlt(this, r.x(), r.y(), &pix); } } void Score::timerEvent(TQTimerEvent *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(TQKeyEvent *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() { TQString up("Up"); up = kapp->config()->readEntry("upKey", up); UpKey = TDEShortcut(up); TQString down("Down"); down = kapp->config()->readEntry("downKey", down); DownKey = TDEShortcut(down); TQString left("Left"); left = kapp->config()->readEntry("leftKey", left); LeftKey = TDEShortcut(left); TQString right("Right"); right = kapp->config()->readEntry("rightKey", right); RightKey = TDEShortcut(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() { TQString oldgroup = kapp->config()->group(); TQString 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; TQString 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; TQTimer::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 = TQDateTime::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)) { TQDataStream 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 = TQString::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 = TQTime(); hallOfFame[i].moment = TQDateTime(); 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 (tdepacmanrc: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 (tdepacmanrc:highscoreFilePath)." ).arg(systemHighscoreFileInfo.filePath()), TQString::null, "PrivateHighscore"); if (highscoreFile.open(IO_WriteOnly)) { TQDataStream 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; TQRect r = bitfont->rect(i18n("PAUSED")); r.moveCenter(TQPoint(this->width()/2, this->height()/2)); repaint(r, TRUE); // repaint 1UP or 2UP repaint(FALSE); } void Score::end() { if (paused) { TQTimer::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 TQString. The format can be changed using internationalization * of the string "YY/MM/DD". Invalid TQDate's where returned as "00/00/00". */ TQString Score::formatDate(TQDate date) { TQString s = i18n("@YY@/@MM@/@DD@"); TQString dd; dd.sprintf("%02d", date.isValid() ? date.year() % 100 : 0); s.replace(TQRegExp("@YY@"), dd); dd.sprintf("%02d", date.isValid() ? date.month() : 0); s.replace(TQRegExp("@MM@"), dd); dd.sprintf("%02d", date.isValid() ? date.day() : 0); s.replace(TQRegExp("@DD@"), dd); return s; } TQRect Score::rect(int col, float row, TQString str, int align) { TQRect 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"-TDEConfig-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. */ TQFileInfo Score::locateHighscoreFilePath() { TQFileInfo systemHighscoreDirPath; TQStringList systemHighscoreDirs; // Schreibfähige "private" highscore-Datei ermitteln für den fallback. privateHighscoreFileInfo.setFile(TDEGlobal::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 = TDEGlobal::dirs()->resourceDirs("appdata"); else systemHighscoreDirs = TQStringList(systemHighscoreFileInfo.filePath()); for (TQStringList::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; } #include "score.moc"