[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--