/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine 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. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.game; import totalcross.io.*; import totalcross.ui.dialog.*; /** * The game highscores management class. <br> * <br> * The highscores are stored in a waba PDBFile object which is linked to the * application through the software creatorID. This causes the highscores * database deletion when the game with the same creatorID is erased. * <p> * The complete database name is composed by the game name 'appl' and its * creatorID, both provided during the engine setup.<br> * The highscores database name is : ${appl}_HSC.${creatorID}.DATA * <p> * You can find a complete game API sample named 'Ping' in the TotalCross samples * folder.<br> * Here is some sample code: * * <pre> * import totalcross.game.*; * import totalcross.util.props.*; * ... * * <i>public class Ping extends <U>GameEngine</U></i> { * * // constructor * public Ping() * { * totalcross.sys.Settings.setPalmOSStyle(true); * * // define the game API setup attributes * * gameName = "Ping"; * * // when not run on device, appCreatorId does not always return the same value. * * gameCreatorID = Settings.onJavaSE ? totalcross.sys.Settings.appCreatorId:"PiNg"; * * gameVersion = 100; // v1.00 * gameHighscoresSize = 7; // store the best 7 highscores * gameRefreshPeriod = 75; // 75 ms refresh periods * gameIsDoubleBuffered = true; // used double buffering to prevent flashing display * gameDoClearScreen = true; // screen is cleared before each frame display * gameHasUI = false; // no UI elements, frame displays are optimized * ... * } * * public void addUserScore(Property.Str user,int score) { * * <B>HighScores</B> hs=<U>getHighScores</U>(); * * // try to add a new score to the highscores * * HighScoreEntry insEntry=<B>hs.add</B>(score,user); * * if (insEntry!=null) { // is score a highscore ? * * // get access to all highscores * HighScoreEntry[] entries=<B>hs.getEntries()</B>; * * for (int n=0; n<<B>hs.size</B>(); n++) { * * // access the nth highscore entry * HighScoreEntry entry=entries[n]; * * Vm.debug("name: "+entry.name+" score="+Convert.toString(entry.score)); * } * * } * * } * </pre> * * @author Frank Diebolt * @version 1.0 */ public class HighScores extends PDBFile { private final static String dbName_suffix = "_HSC."; private final static String dbType = ".DATA"; HighScoreEntry entries[]; private boolean dirty; private int validEntries; // number of valid highscore entries /** * This class must be instantiated using the * GameEngineMainWindow.getHighScores * * @throws totalcross.io.IOException */ protected HighScores(GameEngine engine) throws totalcross.io.IOException { super(engine.gameName + dbName_suffix + engine.gameCreatorID + dbType, PDBFile.CREATE); entries = new HighScoreEntry[engine.gameHighscoresSize]; for (int i = 0; i < entries.length; i++) entries[i] = new HighScoreEntry(this); dirty = false; if (getRecordCount() < 1) return; try { setRecordPos(0); DataStream ds = new DataStream(this); /* int version= */ds.readInt(); /* * this test is not needed until a database format change occurs... if * (version>GameEngine.GAME_ENGINE_VERSION) throw new * GameEngineException("HS:game API "+GameEngine.GAME_ENGINE_VERSION+" out * of date error"); */ validEntries = ds.readInt(); for (int i = 0; i < entries.length; i++) if (i < validEntries) { entries[i].name = ds.readString(); entries[i].score = ds.readInt(); } } catch (IOException e) { MessageBox.showException(e,false); } } /** * Amount of highscore entries. * * @return the number of entries in the highscore table. */ public final int size() { return entries.length; } /** * Retrieve the highscore entries. * * @return an array to the highscore entries. */ public final HighScoreEntry[] getEntries() { return entries; } /** * add a new score to the highscore table. * * @param score * to add. * @param name * of the performer, may be null. * @see HighScoreEntry * @return added entry or null */ public HighScoreEntry add(int score, String name) { int insert; // lookup the position to insert the score for (insert = 0; insert < entries.length; insert++) if (entries[insert].name == null || score > entries[insert].score) break; if (insert >= entries.length) return null; // didn't found a lower score to replace // reuse the last highscore entry, sorry but you are ejected HighScoreEntry free = entries[entries.length - 1]; // create a free entry by moving back the least scores for (int m = entries.length - 2; m >= insert; m--) entries[m + 1] = entries[m]; // store the new score free.name = name; free.score = score; entries[insert] = free; if (validEntries < entries.length) validEntries++; dirty = true; return entries[insert]; } /** * Stores the highscores database. * * @return false if no changes to save or an error occurs */ public boolean save() { if (!dirty) return false; // use this nice object to resize the highscore record ResizeRecord rs = new ResizeRecord(this, 100); try { rs.restartRecord(0); DataStream ds = new DataStream(rs); ds.writeInt(GameEngine.GAME_ENGINE_VERSION); ds.writeInt(validEntries); for (int i = 0; i < validEntries; i++) { ds.writeString(entries[i].name); ds.writeInt(entries[i].score); } rs.endRecord(); } catch (totalcross.io.IOException e) { throw new GameEngineException(e.getMessage()); } dirty = false; return true; } /** * closes the highscores database. * * @throws totalcross.io.IOException */ public void close() throws totalcross.io.IOException { save(); super.close(); } }