[Kde-games-devel] Highscores
Nicolas Hadacek
kde-games-devel@mail.kde.org
Thu, 16 Jan 2003 10:09:17 +0100
--------------Boundary-00=_HRUST3WQAPUU75IWN7XU
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 8bit
Hi there,
I propose to move the highscore framework that I have developped for kmines,
ksirtet & co to the kdegames library. I think this framework is much better
than the current KScoreDialog. I've already ported some other games in my cvs
tree.
I have attached the two public headers ; you can find the complete
implementation in kdegames/kmines/generic/ (gsettings* and gstring* are not
part of it ...).
what do you think ?
cu,
Nicolas
--------------Boundary-00=_HRUST3WQAPUU75IWN7XU
Content-Type: text/x-chdr;
charset="us-ascii";
name="kexthighscore.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="kexthighscore.h"
/*
This file is part of the KDE games library
Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef KEXTHIGHSCORE_H
#define KEXTHIGHSCORE_H
#include <qvaluevector.h>
#include <kurl.h>
#include "ghighscores_item.h"
class QTabWidget;
class QWidget;
namespace KExtHighscore
{
class Score;
class Item;
class ManagerPrivate;
extern ManagerPrivate *internal;
/**
* Get the current game type.
*/
uint gameType();
/**
* Set the current game type.
*/
void setGameType(uint gameType);
/**
* Configure the highscores.
* @return true if the configuration has been modified and saved
*/
bool configure(QWidget *parent);
/**
* Show the highscores lists.
*/
void show(QWidget *parent);
typedef QValueVector<Score> ScoreVector;
/**
* Show scores for a multiplayers game.
*
* Usage example:
* <pre>
* KExtHighscore::ScoreVector scores(2);
* scores[0].setType(KExtHighscore::Won);
* scores[0].setData("score", score1);
* scores[0].setData("name", player1);
* scores[1].setType(KExtHighscore::Lost);
* scores[1].setData("score", score2);
* scores[1].setData("name", player2);
* KExtHighscore::showMultipleScores(scores, widget);
* </pre>
*/
void showMultipleScores(const ScoreVector &scores, QWidget *parent);
/**
* Submit a score. See @ref Manager for usage example.
*
* @param widget a widget used as parent for error message box.
*/
void submitScore(const Score &score, QWidget *widget);
/**
* @return the last score in the local list of highscores. The worst possible
* score if there are less items than the maximum number.
*/
Score lastScore();
/**
* @return the first score in the local list of highscores (the worst possible
* score if there is no entry).
*/
Score firstScore();
/**
* This class manages highscores and players entries (several players can
* share the same highscores list if the libkdegame library is built to
* support a common highscores file; NOTE that to correctly implement such
* feature we probably need a locking mechanism in @ref KHighscore).
*
* You need one instance of this class during the application lifetime ; in
* main() just insert <pre> KExtHighscore::Manager highscoresManager; </pre>
* with the needed arguments. Use the derived class if you need to
* reimplement some of the default methods.
*
* This class has three functions :
* <ul>
* <li> Update the highscores list when new entries are submitted </li>
* <li> Display the highscores list and the players list </li>
* <li> Send query to an optionnal web server to support world-wide
* highscores </li>
* </ul>
*
* The highscores and the players lists contain several items described by
* the @ref Item class.
*
* The highscores list contains by default :
* <ul>
* <li> "id" : the index of the player (internal and not shown) </li>
* <li> "name" : the player name (automatically set from the config value)</li>
* <li> "score" : the score value </li>
* <li> "date" : the time and date of the highscore (automatically set) </li>
* </ul>
* You can add an item or replace the default item for the score value
* (for e.g. displaying it differently) by calling @ref setScoreItem just after
* construction.
*
* The players list contains :
* <ul>
* <li> "name" : the player name (as defined by the user in the configuration
* dialog) </li>
* <li> "nb game" : the number of games </li>
* <li> "mean score" : the mean score </li>
* <li> "best score" : the best score </li>
* <li> "date" : the best score time and date </li>
* <li> "comment" : the player comment (as defined by the user in the
* configuration dialog) </li>
* </ul>
* You can replace the best score and the mean score item
* by calling @ref setPlayerItem just after construction.
*
* To submit a new score at game end, just construct a @ref Score, set the
* score data and then call @ref submitScore.
* <pre>
* KExtHighscore::Score score(KExtHighscore::Won);
* score.setData("score", myScore);
* KExtHighscore::submitScore(score, widget);
* </pre>
* You only need to set the score value ("name" and "date" are set
* automatically) and the value of the items you have optionnally added with
* @ref setScoreItem.
*/
class Manager
{
public:
/**
* Constructor
*
* @param nbGameTypes the number of different game types (usually one).
* For example KMines has easy, normal and expert levels.
* @param maxNbEntries the maximum numbers of highscores entries (by game
* types)
*/
Manager(uint nbGameTypes = 1, uint maxNbEntries = 10);
virtual ~Manager();
/**
* Set the world-wide highscores.
*
* Note: should be called at construction time.
*
* @param url the web server url
* @param version the game version which is sent to the wrb server (it can
* be useful for backward compatibility on the server side).
*/
void setWWHighscores(const KURL &url, const QString &version);
/**
* Set if the number of lost games should be track for the world-wide
* highscores statistics. By default, there is no tracking.
*
* Note: should be called at construction time.
*/
void setTrackLostGames(bool track);
/**
* Set if the statistics tab should be shown in the highscores dialog.
* You only want to show this tab if it makes sense to lose or to win the
* game (for e.g. it makes no sense for a tetris game but it does for a
* minesweeper game).
*
* Note: should be called at construction time.
*/
void showStatistics(bool show);
enum ScoreTypeBound { ScoreNotBound, ScoreBound };
/**
* Set the ranges for the score histogram.
*
* Note: should be called at construction time.
*/
void setScoreHistogram(const QMemArray<uint> &scores, ScoreTypeBound type);
enum ShowMode { AlwaysShow, NeverShow, ShowForHigherScore,
ShowForHighestScore };
/**
* Set how the highscores dialog is shown at game end.
* By default, the mode is @ref ShowAtHigherScore.
*
* Note: should be called at construction time.
*/
void setShowMode(ShowMode mode);
/**
* Score type (@see setScoreType).
* @p Normal default score (unsigned integer without upper bound)
* @p MinuteTime score by time bound at 3599 seconds (for e.g. kmines)
*/
enum ScoreType { Normal, MinuteTime };
/**
* Set score type. Helper method to quickly set the type of score.
*
* Note: should be called at construction time.
*/
void setScoreType(ScoreType type);
/**
* @return true is the first score is strictly worse than the second one.
* By default return s1.score()<s2.score(). You can reimplement
* this method if additionnal items added to @ref Score can further
* differentiate the scores (for e.g. the time spent).
*
* Note that you do not need to use directly this method, simply write
* <pre>s1<s2</pre> since @ref Score::operator< calls this method.
*/
virtual bool isStrictlyLess(const Score &s1, const Score &s2) const;
/**
* Add/replace an item in the highscores list. It will add a column to the
* highscores list.
*
* Note : This method should be called at construction time.
*
* If @p name is "score" the default item will be replaced by the given
* one.
*/
void setScoreItem(const QString &name, Item *item);
/**
* Replace an item in the players list (the @p name should be "best score"
* or "mean score").
*
* Note : This method should be called at construction time.
*/
void setPlayerItem(const QString &name, Item *item);
protected:
/**
* Possible type of label (@see gameTypeLabel).
* @p Standard label used in config file.
* @p I18N label used to display the game type.
* @p WW label used when contacting the world-wide highscores server.
* @p Icon label used to load the icon corresponding to the game type.
*/
enum LabelType { Standard, I18N, WW, Icon };
/**
* @return the label corresponding to the game type. The default
* implementation works only for one game type : you need to reimplement
* this method if the number of game types is more than one.
*/
virtual QString gameTypeLabel(uint gameType, LabelType type) const;
/**
* This method is called once for each player (ie for each user). You
* can reimplement it to convert old style highscores to the new mechanism
* (@see submitLegacyScore). By default this method does nothing.
*
* @param gameType the game type
*/
virtual void convertLegacy(uint gameType) { Q_UNUSED(gameType); }
/**
* This method should be called from @ref convertLegacy. It is used
* to submit an old highscore (it will not be send over the network).
* For each score do something like:
* <pre>
* Score score(Won);
* score.setData("score", oldScore);
* score.setData("name", name);
* submitLegacyScore(score);
* </pre>
* Note that here you can set the player "name" and the highscore "date"
* if they are known.
*/
void submitLegacyScore(const Score &score) const;
/**
* This method is called before submitting a score to the world-wide
* highscores server. You can reimplement this method to add an entry
* with @ref addToQueryURL. By default this method does nothing.
*
* @param score the score to be submitted.
*/
virtual void additionnalQueryItems(KURL &url, const Score &score) const
{ Q_UNUSED(url); Q_UNUSED(score); }
/**
* Add an entry to the url to be submitted (@see additionnalQueryItems).
*
* @param item the item name
* @param content the item content
*/
static void addToQueryURL(KURL &url, const QString &item,
const QString &content);
friend class HighscoresDialog;
friend class ManagerPrivate;
private:
Manager(const Manager &);
Manager &operator =(const Manager &);
};
}; // namespace
#endif
--------------Boundary-00=_HRUST3WQAPUU75IWN7XU
Content-Type: text/x-chdr;
charset="us-ascii";
name="kexthighscore_item.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="kexthighscore_item.h"
/*
This file is part of the KDE games library
Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef KEXTHIGHSCORE_ITEM_H
#define KEXTHIGHSCORE_ITEM_H
#include <qvariant.h>
#include <qmap.h>
#include <qnamespace.h>
namespace KExtHighscore
{
class ItemArray;
//-----------------------------------------------------------------------------
/**
* This class defines how to convert and how to display
* a highscore element (such as the score, the date, ...) or a player
* info (such as the player name, the best score, ...).
*/
class Item
{
public:
/**
* Possible display format.
* <ul>
* <li> @p NoFormat : no formatting (default) </li>
* <li> @p OneDecimal : with one decimal (only for Double) </li>
* <li> @p Percentage : with one decimal + % (only for Double) </li>
* <li> @p MinuteTime : MM:SS ie 3600 is 00:00, 1 is 59:59 and 0 is
* undefined (only for UInt, Int and Double) </li>
* <li> @p DateTime : date and time according to locale (only for
* DateTime) </li>
* </ul>
*/
enum Format { NoFormat, OneDecimal, Percentage, MinuteTime,
DateTime };
/**
* Possible special value for display format.
* <ul>
* <li> @p NoSpecial : no special value ; a null DateTime is replaced by
* "--" (default) </li>
* <li> ZeroNotDefine : 0 is replaced by "--" (only for UInt, Int and
* Double) </li>
* <li> @p NegativeNotDefined : negative values are replaced by "--" (only
* for Int and Double) </li>
* <li> @p Anonymous : replace the special value @ref ItemBase::ANONYMOUS
* by i18n("anonymous") (only for String) </li>
* </ul>
*/
enum Special { NoSpecial, ZeroNotDefined, NegativeNotDefined,
Anonymous };
/**
* Constructor.
*
* @param def default value ; the QVariant also gives the type of data.
* Be sure to cast the value to the required type (for e.g. with uint).
* @param label the label corresponding to the item. If empty, the item
* is not shown.
* @param alignment the alignment of the item.
*/
Item(const QVariant &def = QVariant::Invalid,
const QString &label = QString::null, int alignment = Qt::AlignRight);
virtual ~Item();
/**
* Set the display format.
* @see Format
*/
void setPrettyFormat(Format format);
/**
* Set the special value for display.
* @see Special
*/
void setPrettySpecial(Special special);
/**
* @return if the item is shown.
*/
bool isVisible() const { return !_label.isEmpty(); }
/**
* @return the label.
*/
QString label() const { return _label; }
/**
* @return the alignment.
*/
int alignment() const { return _alignment; }
/**
* @return the default value.
*/
const QVariant &defaultValue() const { return _default; }
/**
* @return the converted value (by default the value is left
* unchanged). Most of the time you don't need to reimplement this method.
*
* @param i the element index ("rank" for score / "id" for player)
*/
virtual QVariant read(uint i, const QVariant &value) const;
/**
* @return the string to be displayed. You may need to reimplement this
* method for special formatting (different from the standard ones).
*
* @param i the element index ("rank" for score / "id" for player)
*/
virtual QString pretty(uint i, const QVariant &value) const;
private:
QVariant _default;
QString _label;
int _alignment;
Format _format;
Special _special;
class ItemPrivate;
ItemPrivate *d;
static QString timeFormat(uint);
};
//-----------------------------------------------------------------------------
/**
* @ref Item for the score. By default no special formating.
*/
class ScoreItem : public Item
{
public:
ScoreItem(uint worstScore = 0);
};
/**
* @ref Item for mean score. By default, only show one decimal and
* 0 is shown as "--"
*/
class MeanScoreItem : public Item
{
public:
MeanScoreItem();
};
/**
* @ref Item for the best highscore. 0 is shown as "--".
*/
class BestScoreItem : public Item
{
public:
BestScoreItem();
};
/**
* Optionnal @ref Item for elapsed time (maximum value is 3599 seconds).
*/
class ElapsedTimeItem : public Item
{
public:
ElapsedTimeItem();
};
//-----------------------------------------------------------------------------
/**
* Manage an array of data associated with an @ref Item.
*/
class DataArray
{
public:
~DataArray();
/**
* @return the data associated with the named @ref Item.
*/
const QVariant &data(const QString &name) const;
/**
* Set the data associated with the named @ref Item. Note that the
* value should have the type of the default value of the @ref
* Item.
*/
void setData(const QString &name, const QVariant &value);
protected:
/**
* @internal
*/
DataArray(const ItemArray &items);
private:
QMap<QString, QVariant> _data;
class DataArrayPrivate;
DataArrayPrivate *d;
friend QDataStream &operator <<(QDataStream &, const DataArray &);
friend QDataStream &operator >>(QDataStream &, DataArray &);
};
QDataStream &operator <<(QDataStream &stream, const DataArray &array);
QDataStream &operator >>(QDataStream &stream, DataArray &array);
//-----------------------------------------------------------------------------
/**
* Possible score type.
* @p Won the game has been won.
* @p Lost the game has been lost or has been aborted.
*/
enum ScoreType { Won = 0, Lost = -1 };
/**
* This class contains data for a score. You should not inherit from
* this class but reimplement the methods in @ref Highscores.
*/
class Score : public DataArray
{
public:
/**
* Constructor.
*/
Score(ScoreType type = Won);
~Score();
/**
* @return the game type.
*/
ScoreType type() const { return _type; }
/**
* Set the game type.
*/
void setType(ScoreType type) { _type = type; }
/**
* @return the score value.
* Convenience function equivalent to <pre>data("score").toUInt()</pre>
*/
uint score() const { return data("score").toUInt(); }
/**
* @return true if this is the worst possible score (ie the default
* argument of @ref ScoreItem).
*/
bool isTheWorst() const;
/**
* Convenience comparison operator equivalent to
* <pre>Manager::isStrictlyLess(*this, score)</pre>
*/
bool operator <(const Score &score) const;
private:
ScoreType _type;
class ScorePrivate;
ScorePrivate *d;
friend QDataStream &operator <<(QDataStream &stream, const Score &score);
friend QDataStream &operator >>(QDataStream &stream, Score &score);
};
QDataStream &operator <<(QDataStream &stream, const Score &score);
QDataStream &operator >>(QDataStream &stream, Score &score);
}; // namespace
#endif
--------------Boundary-00=_HRUST3WQAPUU75IWN7XU--