highscore list implemented

git-svn-id: https://svn.code.sf.net/p/extremetuxracer/code/trunk@231 0420edf4-82e4-42fc-9478-35b55e6d67a3
master
erin10 2010-11-27 15:38:51 +00:00
parent e4454a8ee5
commit 7bf4554855
16 changed files with 412 additions and 56 deletions

View File

@ -31,7 +31,7 @@ GNU General Public License for more details.
#define NMLPOINT(x,y) MakeVector (XCD(x), ELEV(x,y), ZCD(y) )
#define MAX_COURSES 164
#define MAX_COURSES 64
#define MAX_TERR_TYPES 64
#define MAX_COLL 12192
#define MAX_NOCOLL 8192

14
doc/score_algorithm Normal file
View File

@ -0,0 +1,14 @@
You may want to know how the score points are calculated. Sometimes you have to decide between a shorter time or an additional herring. For this reason, I've tried to make the algorithm as transparent as possible.
The basis rule is clear and easy:
1 herring - 10 points
1 second gain of time - 10 points (with resolution 0.1 second - 1 point)
Score is the sum of herring points and time points.
The herring points are calculated absolutely, starting with 0. For the time points we need a reference value; the difference between this value and the reached time is the result. Apparently the reference value must be high enough so that we don't get negative results. Additionally the reference should depend from the course length: the larger the course the higher the value. In the algorithm the reference is proportional to the course length, divided by 10.
Example: The course "Bunny Hill" is 520 units long, thus the reference time value is 52. If you reach the finish line after 31.3 seconds, you get (52 - 31.3) * 10 = 207 time points. With 21 herrings the score is 417.
This algorithm seems to be rough and inexact, and it doesn't allow to compare the results of different courses. That's right, but the courses differ too much. All attempts to make the race results comparable can't be successful. You would have to work with weighted values for time and herrings, so that the user won't be able to evaluate the situation when racing.

View File

@ -143,6 +143,7 @@ typedef enum {
CREDITS,
GAME_CONFIG,
HELP,
SCORE,
TOOLS,
NUM_GAME_MODES
} TGameMode;

View File

@ -117,13 +117,15 @@ CFont::CFont () {
curr_size = 20; // default size: 20 px
}
CFont::~CFont () {
void CFont::Clear () {
for (int i=0; i<MAX_FONTS; i++) {
if (fonts[i] != NULL) delete (fonts[i]);
}
numFonts = 0;
fontindex = "";
}
}
CFont::~CFont () {}
// --------------------------------------------------------------------
// private
@ -192,6 +194,7 @@ void CFont::UnicodeStr (wchar_t *buff, const char *string) {
int CFont::LoadFont (string name, const char *path) {
if (numFonts >= MAX_FONTS) return -1;
fonts[numFonts] = new FTGLPixmapFont (path);
// fonts[numFonts] = new FTGLTextureFont (path);
if (fonts[numFonts]->Error()) {
Message ("Failed to open font");
return -1;

1
font.h
View File

@ -47,6 +47,7 @@ public:
CFont ();
~CFont ();
void Clear ();
int LoadFont (string name, const char *dir, const char *filename);
int LoadFont (string name, const char *path);
bool LoadFontlist ();

View File

@ -208,7 +208,8 @@ const FTLibrary &FTLibrary::Instance () {
}
FTLibrary::~FTLibrary () {
if (library != 0) {
// PrintStr ("desctructor FTLibrary");
if (library != 0) {
FT_Done_FreeType (*library);
delete library;
library= 0;
@ -240,10 +241,11 @@ FTFace::FTFace (const char* fontFilePath)
err = FT_New_Face (*FTLibrary::Instance().GetLibrary(), fontFilePath, DEFAULT_FACE_INDEX, ftFace);
if (err) {
delete ftFace;
Message ("error FT_New_Face");
delete ftFace;
ftFace = 0;
} else {
numGlyphs = (*ftFace)->num_glyphs;
numGlyphs = (*ftFace)->num_glyphs;
hasKerningTable = FT_HAS_KERNING((*ftFace));
}
}
@ -265,8 +267,8 @@ FTFace::FTFace (const unsigned char *pBufferBytes, size_t bufferSizeInBytes)
}
FTFace::~FTFace() {
if (ftFace) {
FT_Done_Face (*ftFace);
if (ftFace) {
FT_Done_Face (*ftFace);
delete ftFace;
ftFace = 0;
}

View File

@ -423,7 +423,6 @@ public:
FT_GlyphSlot Glyph(unsigned int index, FT_Int load_flags);
unsigned int GlyphCount() const { return numGlyphs;}
FT_Error Error() const { return err; }
private:
FT_Face* ftFace;
FTSize charSize;

View File

@ -32,20 +32,11 @@ GNU General Public License for more details.
#include "game_ctrl.h"
#include "translation.h"
#include "keyframe.h"
#include "score.h"
static TVector2 cursor_pos = {0, 0 };
static CKeyframe *final_frame;
void CalcScore (CControl *ctrl){
double par_time;
double w, l;
Course.GetDimensions (&w, &l);
par_time = l * 0.03;
g_game.score = max (0, (int)(100 * (par_time - g_game.time)
+ 200 * g_game.herring) );
}
void QuitGameOver () {
if (g_game.game_type == PRACTICING) {
Winsys.SetMode (RACE_SELECT);
@ -63,23 +54,6 @@ static void mouse_cb (int button, int state, int x, int y) {
QuitGameOver ();
}
void CalcRaceResult () {
g_game.race_result = -1;
if (g_game.time <= g_game.time_req.x &&
g_game.herring >= g_game.herring_req.i) g_game.race_result = 0;
if (g_game.time <= g_game.time_req.y &&
g_game.herring >= g_game.herring_req.j) g_game.race_result = 1;
if (g_game.time <= g_game.time_req.z &&
g_game.herring >= g_game.herring_req.k) g_game.race_result = 2;
double ll, ww;
Course.GetDimensions (&ww, &ll);
double herringpt = g_game.herring * 10;
double timept = ll - (g_game.time * 10);
g_game.score = (int)(herringpt + timept);
if (g_game.score < 0) g_game.score = 0;
}
void GameOverMotionFunc (int x, int y) {
TVector2 old_pos;
@ -94,7 +68,6 @@ void GameOverMotionFunc (int x, int y) {
void GameOverMessage () {
int hh = param.y_resolution;
char buff[64];
CControl *ctrl = Players.GetCtrl (g_game.player_id);
int fheight = 75;
int fwidth = 500;
char s[128];
@ -104,7 +77,6 @@ void GameOverMessage () {
if (!g_game.raceaborted) {
if (g_game.game_type == PRACTICING) {
CalcScore (ctrl);
sprintf (buff, "%d", g_game.score);
strcpy (s, "Score: ");
strcat (s, buff);
@ -146,7 +118,7 @@ void GameOverMessage () {
// =========================================================================
void GameOverInit (void) {
Sound.HaltAll ();
CalcRaceResult ();
if (g_game.game_type == CUPRACING) {
if (g_game.race_result >= 0) {
Music.PlayTheme (g_game.theme_id, MUS_WONRACE);
@ -161,13 +133,17 @@ void GameOverInit (void) {
}
}
if (!g_game.raceaborted) Score.CalcRaceResult ();
if (g_game.raceaborted || !g_game.use_keyframe) {
final_frame = NULL;
} else {
if (g_game.game_type == CUPRACING) {
if (g_game.race_result < 0) final_frame = Char.GetKeyframe (g_game.char_id, LOSTRACE);
if (g_game.race_result < 0) final_frame =
Char.GetKeyframe (g_game.char_id, LOSTRACE);
else final_frame = Char.GetKeyframe (g_game.char_id, WONRACE);
} else final_frame = Char.GetKeyframe (g_game.char_id, FINISH);
if (!g_game.raceaborted) {
CControl *ctrl = Players.GetCtrl (g_game.player_id);
final_frame->Init (ctrl->cpos, -0.18);

View File

@ -39,7 +39,8 @@ void QuitGameType (int sc) {
case 2: Winsys.SetMode (GAME_CONFIG); break;
case 3: Winsys.SetMode (CREDITS); break;
case 4: Winsys.SetMode (HELP); break;
case 5: Winsys.Quit (); break;
case 5: Winsys.SetMode (SCORE); break;
case 6: Winsys.Quit (); break;
};
}
@ -85,7 +86,7 @@ void GameSelectMotionFunc (int x, int y) {
static void GameSelectInit (void) {
Winsys.ShowCursor (!param.ice_cursor);
init_ui_snow ();
scope = 0;
scope = 1;
ResetWidgets ();
double top = (double)(param.y_resolution * 0.4);
@ -95,9 +96,10 @@ static void GameSelectInit (void) {
AddTextButton (Trans.Text(3), CENTER, top + dist * 2, 2, FIT);
AddTextButton (Trans.Text(4), CENTER, top + dist * 3, 3, FIT);
AddTextButton (Trans.Text(43), CENTER, top + dist * 4, 4, FIT);
AddTextButton (Trans.Text(5), CENTER, top + dist * 5, 5, FIT);
AddTextButton ("Highscore list", CENTER, top + dist * 5, 5, FIT);
AddTextButton (Trans.Text(5), CENTER, top + dist * 6, 6, FIT);
Music.Play (param.menu_music, -1);
g_game.loopdelay = 20;
g_game.loopdelay = 10;
}
static void GameSelectLoop (double time_step) {
@ -127,6 +129,7 @@ static void GameSelectLoop (double time_step) {
PrintTextButton (3, scope);
PrintTextButton (4, scope);
PrintTextButton (5, scope);
PrintTextButton (6, scope);
if (param.ice_cursor) DrawCursor ();
Reshape (ww, hh);

View File

@ -40,6 +40,7 @@ GNU General Public License for more details.
#include "regist.h"
#include "keyframe.h"
#include "newplayer.h"
#include "score.h"
TGameData g_game;
@ -111,6 +112,7 @@ int main( int argc, char **argv ) {
RegisterKeyInfo ();
RegisterToolFuncs ();
NewPlayerRegister ();
RegisterScoreFunctions ();
// theses resources must or should be loaded before splashscreen starts
Course.MakeStandardPolyhedrons ();

View File

@ -20,10 +20,10 @@ GNU General Public License for more details.
#include "bh.h"
#define MAX_PADDLING_SPEED (60.0 / 3.6) // original 60
#define PADDLE_FACT 1.0 // original 1.0
#define MAX_PADDLING_SPEED (60.0 / 3.6) /* original 60 */
#define PADDLE_FACT 1.0 /* original 1.0 */
#define EARTH_GRAV 9.81
#define EARTH_GRAV 9.81 /* ok, why not 10.0 ? */
#define JUMP_FORCE_DURATION 0.20
#define TUX_MASS 20
#define MIN_TUX_SPEED 1.4

View File

@ -37,6 +37,7 @@ static int last_character;
static int xleft, ytop;
static int curr_player = 0;
static int last_player;
static int old_last;
void QuitRegistration () {
Players.ResetControls ();
@ -67,6 +68,7 @@ void RegistKeys (unsigned int key, bool special, bool release, int x, int y) {
case 27: Winsys.Quit (); break;
case 13:
if (curr_focus == 3) {
old_last = last_player;
Winsys.SetMode (NEWPLAYER);
} else QuitRegistration (); break;
case SDLK_TAB:
@ -86,7 +88,7 @@ void RegistMouseFunc (int button, int state, int x, int y) {
case 0: ChangeRegistSelection (foc, dir); break;
case 1: ChangeRegistSelection (foc, dir); break;
case 2: QuitRegistration (); break;
case 3: Winsys.SetMode (NEWPLAYER); break;
case 3: old_last = last_player; Winsys.SetMode (NEWPLAYER); break;
}
}
}
@ -127,8 +129,9 @@ void RegistInit (void) {
CharList = Char.CharList;
last_character = Char.numCharacters - 1;
last_player = Players.numPlayers - 1;
if (g_game.prev_mode == NEWPLAYER) curr_player = last_player;
else curr_player = g_game.start_player;
if (g_game.prev_mode == NEWPLAYER && old_last != last_player) {
curr_player = last_player;
} else curr_player = g_game.start_player;
}
void RegistLoop (double timestep ){

320
score.cpp
View File

@ -19,7 +19,6 @@ GNU General Public License for more details.
#include "textures.h"
#include "audio.h"
#include "gui.h"
#include "course.h"
#include "tux.h"
#include "env.h"
#include "particles.h"
@ -28,10 +27,321 @@ GNU General Public License for more details.
#include "game_ctrl.h"
#include "translation.h"
CScore Score;
CScore::CScore () {}
void CScore::ResetScorelist (int list_idx) {
TScoreList *list = &Scorelist[list_idx];
for (int i=0; i<MAX_SCORES; i++) {
list->scores[i].player = "";
list->scores[i].points = 0;
list->scores[i].herrings = 0;
list->scores[i].time = 0.0;
}
list->numScores = 0;
}
void CScore::AddScore (int list_idx, TScore score) {
if (list_idx < 0 || list_idx >= MAX_COURSES) return;
if (score.points < 1) return;
TScoreList *list = &Scorelist[list_idx];
int num = list->numScores;
int i;
int pos = 0;
int lastpos = num-1;
int val = score.points;
if (num == 0) {
list->scores[0] = score;
list->numScores++;
} else if (num == MAX_SCORES) {
while (val <= list->scores[pos].points && pos < num) pos++;
if (pos == lastpos) {
list->scores[pos] = score;
} else if (pos < lastpos) {
for (i=lastpos; i>pos; i--) list->scores[i] = list->scores[i-1];
list->scores[pos] = score;
}
} else {
while (val <= list->scores[pos].points && pos < num) pos++;
for (i=num; i>pos; i--) list->scores[i] = list->scores[i-1];
list->scores[pos] = score;
list->numScores++;
}
}
void CScore::PrintScorelist (int list_idx) {
if (list_idx < 0 || list_idx >= MAX_COURSES) return;
TScoreList *list = &Scorelist[list_idx];
string line;
if (list->numScores < 1) {
PrintString ("no entries in this score list");
} else {
for (int i=0; i<list->numScores; i++) {
line = "player: " + list->scores[i].player;
line += " points: " + Int_StrN (list->scores[i].points);
line += " herrings: " + Int_StrN (list->scores[i].herrings);
line += " time: " + Float_StrN (list->scores[i].time, 2);
PrintString (line);
}
}
}
void CScore::SetScorelist (int list_idx) {
if (list_idx < 0 || list_idx >= MAX_COURSES) return;
ResetScorelist (list_idx);
}
TScoreList *CScore::GetScorelist (int list_idx) {
if (list_idx < 0 || list_idx >= MAX_COURSES) return NULL;
return &Scorelist[list_idx];
}
bool CScore::SaveHighScore () {
CSPList splist (520);
string line;
int li, sc, num;
string coursedir;
TCourse *courselist = Course.CourseList;
TScoreList *lst;
TScore score;
for (li=0; li<MAX_COURSES; li++) {
lst = &Scorelist[li];
if (lst != NULL) {
coursedir = courselist[li].dir;
num = lst->numScores;
if (num > 0) {
for (sc=0; sc<num; sc++) {
score = lst->scores[sc];
line = "*[course] " + courselist[li].dir;
line += " [plyr] " + score.player;
line += " [pts] " + Int_StrN (score.points);
line += " [herr] " + Int_StrN (score.herrings);
line += " [time] " + Float_StrN (score.time, 1);
splist.Add (line);
}
}
}
}
if (!splist.Save (param.config_dir, "highscore")) {
Message ("could not save highscore list");
return false;
}
return true;
}
bool CScore::LoadHighScore () {
CSPList list (520);
string line, course;
int i, cidx;
TScore score;
if (!list.Load (param.config_dir, "highscore")) {
Message ("could not load highscore list");
return false;
}
for (i=0; i<MAX_COURSES; i++) ResetScorelist (i);
for (i=0; i<list.Count(); i++) {
line = list.Line (i);
course = SPStrN (line, "course", "unknown");
cidx = Course.GetCourseIdx (course);
score.player = SPStrN (line, "plyr", "unknown");
score.points = SPIntN (line, "pts", 0);
score.herrings = SPIntN (line, "herr", 0);
score.time = SPFloatN (line, "time", 0);
Score.AddScore (cidx, score);
}
return true;
}
// immmer in init
int CScore::CalcRaceResult () {
g_game.race_result = -1;
if (g_game.time <= g_game.time_req.x &&
g_game.herring >= g_game.herring_req.i) g_game.race_result = 0;
if (g_game.time <= g_game.time_req.y &&
g_game.herring >= g_game.herring_req.j) g_game.race_result = 1;
if (g_game.time <= g_game.time_req.z &&
g_game.herring >= g_game.herring_req.k) g_game.race_result = 2;
double ll, ww;
Course.GetDimensions (&ww, &ll);
double herringpt = g_game.herring * 10;
double timept = ll - (g_game.time * 10);
g_game.score = (int)(herringpt + timept);
if (g_game.score < 0) g_game.score = 0;
TempScore.points = g_game.score;
TempScore.herrings = g_game.herring;
TempScore.time = g_game.time;
TempScore.player = Players.GetName (g_game.player_id);
AddScore (g_game.course_id, TempScore);
return g_game.race_result;
}
// --------------------------------------------------------------------
// score screen
// --------------------------------------------------------------------
static int curr_focus = 0;
static TVector2 cursor_pos = {0, 0};
static int xleft, ytop;
static TCourse *CourseList;
static int lastCourse = 0;
static int curr_course = 0;
void ChangeScoreSelection (int focus, int dir) {
if (dir == 0) {
if (curr_course > 0) curr_course--;
} else {
if (curr_course < lastCourse) curr_course++;
}
}
static TScore aaa;
void ScoreKeys (unsigned int key, bool special, bool release, int x, int y) {
if (release) return;
switch (key) {
case 27: Winsys.SetMode (GAME_TYPE_SELECT); break;
case SDLK_q: Winsys.Quit (); break;
case SDLK_DOWN: ChangeScoreSelection (curr_focus, 1); break;
case SDLK_UP: ChangeScoreSelection (curr_focus, 0); break;
case SDLK_LEFT: ChangeScoreSelection (curr_focus, 0); break;
case SDLK_RIGHT: ChangeScoreSelection (curr_focus, 1); break;
case SDLK_s: Score.SaveHighScore (); break;
case SDLK_l: Score.LoadHighScore (); break;
// for testing:
case SDLK_F12:
aaa.player = Players.GetName (g_game.player_id);
aaa.points = IRandom (1, 10000);
aaa.herrings = IRandom (15, 70);
aaa.time = XRandom (20, 160);
Score.AddScore (curr_course, aaa);
break;
}
}
void ScoreMouseFunc (int button, int state, int x, int y) {
int foc, dir;
if (state == 1) {
GetFocus (x, y, &foc, &dir);
ChangeScoreSelection (foc, dir);
}
}
void ScoreMotionFunc (int x, int y ){
TVector2 old_pos;
int sc, dir;
if (Winsys.ModePending ()) return;
GetFocus (x, y, &sc, &dir);
if (sc >= 0) curr_focus = sc;
y = param.y_resolution - y;
old_pos = cursor_pos;
cursor_pos = MakeVector2 (x, y);
if (old_pos.x != x || old_pos.y != y) {
if (param.ui_snow) push_ui_snow (cursor_pos);
}
}
void ScoreInit (void) {
Winsys.ShowCursor (!param.ice_cursor);
Winsys.KeyRepeat (true);
init_ui_snow ();
Music.Play (param.menu_music, -1);
xleft = (param.x_resolution - 500) / 2;
ytop = AutoYPos (150);
CourseList = Course.CourseList;
lastCourse = Course.numCourses - 1;
curr_course = g_game.course_id;
ResetWidgets ();
AddArrow (xleft + 470, ytop+80, 0, 0);
AddArrow (xleft + 470, ytop+98, 1, 0);
g_game.loopdelay = 1;
}
const string ordinals[10] =
{"1:st", "2:nd", "3:rd", "4:th", "5:th", "6:th", "7:th", "8:th", "9:th", "10:th"};
void ScoreLoop (double timestep ){
int ww = param.x_resolution;
int hh = param.y_resolution;
Music.Update ();
check_gl_error();
ClearRenderContext ();
set_gl_options (GUI);
SetupGuiDisplay ();
update_ui_snow (timestep);
draw_ui_snow();
Tex.Draw (BOTTOM_LEFT, 0, hh - 256, 1);
Tex.Draw (BOTTOM_RIGHT, ww-256, hh-256, 1);
Tex.Draw (TOP_LEFT, 0, 0, 1);
Tex.Draw (TOP_RIGHT, ww-256, 0, 1);
Tex.Draw (T_TITLE_SMALL, -1, 20, 1.0);
if (param.use_papercut_font > 0) FT.SetSize (42); else FT.SetSize (33);
FT.SetColor (colWhite);
FT.DrawString (-1, ytop, "Highscore list");
DrawFrameX (xleft, ytop+76, 460, 44, 3, colMBackgr, colDYell, 1.0);
if (param.use_papercut_font > 0) FT.SetSize (28); else FT.SetSize (22);
FT.SetColor (colWhite);
FT.DrawString (xleft+20, ytop+80, CourseList[curr_course].name);
PrintArrow (0, (curr_course > 0));
PrintArrow (1, (curr_course < lastCourse));
TScoreList *list = Score.GetScorelist (curr_course);
string line;
int y;
FT.SetColor (colWhite);
if (list != NULL) {
if (param.use_papercut_font > 0) FT.SetSize (22); else FT.SetSize (16);
if (list->numScores < 1) {
FT.DrawString (-1, ytop + 140, "No entries for this race");
} else {
if (list->numScores > MAX_SCORES) list->numScores = MAX_SCORES;
for (int i=0; i<list->numScores; i++) {
y = ytop + i*30 + 140;
FT.DrawString (xleft, y, ordinals[i]);
FT.DrawString (xleft+50, y, Int_StrN (list->scores[i].points));
FT.DrawString (xleft + 115, y, list->scores[i].player);
FT.DrawString (xleft + 250, y,
Int_StrN (list->scores[i].herrings) + " herrings");
FT.DrawString (xleft + 375, y,
Float_StrN (list->scores[i].time, 1) + " sec");
}
}
} else Message ("score list out of range");
if (param.ice_cursor) DrawCursor ();
Winsys.SwapBuffers();
}
void ScoreTerm () {}
void RegisterScoreFunctions () {
Winsys.SetModeFuncs (SCORE, ScoreInit, ScoreLoop, ScoreTerm,
ScoreKeys, ScoreMouseFunc, ScoreMotionFunc, NULL, NULL, NULL);
}

37
score.h
View File

@ -18,6 +18,43 @@ GNU General Public License for more details.
#define SCORE_H
#include "bh.h"
#include "course.h"
#define MAX_SCORES 8
// MAX_SCORE_LISTS = MAX_COURSES = 64
typedef struct {
string player;
int points;
int herrings;
double time;
} TScore;
typedef struct {
TScore scores[MAX_SCORES];
int numScores;
} TScoreList;
class CScore {
private:
TScoreList Scorelist[MAX_COURSES];
void ResetScorelist (int list_idx);
TScore TempScore;
public:
CScore ();
void SetScorelist (int list_idx);
void AddScore (int list_idx, TScore score);
TScoreList *GetScorelist (int list_idx);
void PrintScorelist (int list_idx);
bool SaveHighScore ();
bool LoadHighScore ();
int CalcRaceResult ();
};
extern CScore Score;
// --------------------------------------------------------------------
void RegisterScoreFunctions ();
#endif

View File

@ -28,6 +28,7 @@ GNU General Public License for more details.
#include "font.h"
#include "game_ctrl.h"
#include "translation.h"
#include "score.h"
static int xleft, ytop;
@ -68,6 +69,7 @@ void SplashLoop (double timestep ){
Course.LoadTerrainTypes ();
Env.LoadEnvironmentList ();
Course.LoadCourseList ();
Score.LoadHighScore (); // after LoadCourseList !!!
Events.LoadEventList ();
Players.LoadAvatars (); // before LoadPlayers !!!
Players.LoadPlayers ();

View File

@ -20,6 +20,7 @@ GNU General Public License for more details.
#include "audio.h"
#include "game_ctrl.h"
#include "font.h"
#include "score.h"
#define USE_JOYSTICK true
@ -147,8 +148,10 @@ void CWinsys::SetFonttype () {
void CWinsys::Quit () {
if (joystick_active) SDL_JoystickClose (joystick);
Audio.Close ();
SDL_Quit ();
FT.Clear ();
Players.SavePlayers ();
Score.SaveHighScore ();
SDL_Quit ();
exit (0);
}