Previous Blog Next Blog
Prev/Next Blog
by date

TGB High Scores Object

TGB High Scores Object
Name:David Higgins
Date Posted:Nov 24, 2006
Rating:4.5 out of 5
Public:YES
Comments:YES
RSS Feed:GarageGames Blog feedor Subscribe with .
Profile Page:View profile page for David Higgins

Blog post
I was browsing the TGB TDN the other day, and noticed that there was a request for a High Scores Table article. I immediately went, "Hey, I need one of those in my game" and proceeded to code a quick HighScores object in TorqueScript. After writing the object, I decided to post a quick and dirty tutorial on how I created it, and attached the completed highScores.cs to the resource. This resource can be located here, Simple High Scores Object.

As this resource was posted under the TGB TDN, and the script object is not specific to TGB, I figured I'd post it here as well, sort of a "what I've been up to over the holiday", so here's the object:


// FUNCTION: NewHighScores
// PARAMS : None
// DESC : Create's a new HighScores object
// USE : public
function NewHighScores()
{
if(isObject(%this)) return %this;

%obj = new ScriptObject()
{
class = "HighScores";
MaxCount = 10; // DEFAULT
};
return %obj;
}

// FUNCTION: HighScores::setMaximumScores
// PARAMS :
// %this -- object for reference
// %count -- maximum number of scores to store
// DESC : sets the maximum number of scores to store
// USE : public
function HighScores::setMaximumScores(%this, %count)
{
%this.MaxCount = %count;
}

// FUNCTION: HighScores::getMaximumScores
// PARAMS :
// %this -- object for reference
// DESC : returns the number of maximum scores stored
// USE : public
function HighScores::getMaximumScores(%this)
{
return %this.MaxCount;
}

// FUNCTION: HighScores::add
// PARAMS :
// %this -- object for reference
// %player -- player object
// must contain a PlayerName and PlayerScore Field
// fields should not equate to empty strings (!$=)
// DESC : adds a player to the high score stack
// and returns true/false if they have a high score
// USE : public
function HighScores::add(%this, %player)
{
if(isObject(%player))
{
if(%player.PlayerName !$= "")
{
if(%player.PlayerScore !$= "")
{
if(!isObject(%this.Scores)) %this.Scores = new SimSet();
// players score is lower then lowest score
// only check if current score count is equal or greater then max score count
if(%this.Scores.getCount() >= %this.MaxCount)
{
if(%this.Scores.getObject(%this.MaxCount - 1).PlayerScore > %player.PlayerScore) return false;
}

%s = new SimObject()
{
playerName = %player.PlayerName;
playerScore = %player.PlayerScore;
};
%this.Scores.add(%s);
%this.Sort();
while(%this.Scores.getCount() > %this.MaxCount)
{
%ref = %this.Scores.getObject(%this.Scores.getCount()-1);
%this.Scores.remove(%ref);
%ref.delete();
}
return true;
} else { echo("no player score"); return false; }
} else { echo("no player name"); return false; }
} else { echo("player is not object"); return false; }
}

// FUNCTION: HighScores::Sort
// PARAMS :
// %this -- object for reference
// DESC : sorts the high score stack from Top to Bottom
// USE : internal
function HighScores::Sort(%this)
{
if(!isObject(%this.Scores)) { echo("Scores is not object"); return false; }
%scores = HighScores::copySimSet(%this.Scores);
%this.Scores.clear();
echo("Scores.getCount() : " @ %this.Scores.getCount());
echo("%scores.getCount(): " @ %scores.getCount());
while(%scores.getCount() > 0)
{
%high = null;
%highx = -1;
for(%x = 0; %x < %scores.getCount(); %x++)
{
if(%highx == -1) { %high = %scores.getObject(%x).PlayerScore; %highx = %x; }
else {
if(%scores.getObject(%x).PlayerScore >= %high)
{
%highx = %x;
%high = %scores.getObject(%x).PlayerScore;
}
}
}
%this.Scores.add(%scores.getObject(%highx));
%scores.remove(%scores.getObject(%highx));
}
%scores.delete(); // Jeremy
}

// FUNCTION: HighScores::getScore
// PARAMS :
// %this -- object for reference
// %rank -- the high score rank to return
// DESC : returns the score of %rank
// USE : public
function HighScores::getScore(%this, %rank)
{
if(!isObject(%this.Scores)) return -1;
if(%this.Scores.getCount() < %rank - 1) return -1;
return %this.Scores.getObject(%rank).PlayerScore;
}

// FUNCTION: HighScores::getPlayer
// PARAMS :
// %this -- object for reference
// %rank -- rank of player object to return
// DESC : returns player object with %rank score
// USE : public
function HighScores::getPlayer(%this, %rank)
{
if(!isObject(%this.Scores)) return -1;
if(%this.Scores.getCount() < %rank - 1) return -1;
return %this.Scores.getObject(%rank).PlayerName;
}

// FUNCTION: HighScores::getPlayerScore
// PARAMS :
// %this -- object for reference
// %rank -- rank of score to return
// DESC : returns score of player at %rank
// USE : public
function HighScores::getPlayerScore(%this, %rank)
{
if(!isObject(%this.Scores)) return -1;
if(%this.Scores.getCount() < %rank - 1) return -1;
return %this.Scores.getObject(%rank);
}

// FUNCTION: HighScores::getRank
// PARAMS :
// %this -- object for reference
// %player -- player to locate
// DESC : returns rank for %player
// USE : public
function HighScores::getRank(%this, %player)
{
if(!isObject(%this.Scores)) return -1;
if(%player.PlayerName $= "") return -1;
for(%x = 0; %x < %this.Scores.getCount(); %x++)
{
if(%this.Scores.getObject(%x).PlayerName $= %player.PlayerName) return %x; // rank
}
}

// FUNCTION: HighScores::save
// PARAMS :
// %this -- object for reference
// %path -- path to save as
// DESC : saves high score object to %path
// USE : public
function HighScores::save(%this, %path)
{
if(!isObject(%this.Scores)) return false;

%save = new SimSet(HighScoreSavedSet);
%save.add(%this);
for(%x=0;%x<%this.Scores.getCount();%x++)
{
%save.add(%this.Scores.getObject(%x));
}
return %save.save(%path);
}

// FUNCTION: HighScores::load
// PARAMS :
// %path -- path to save file
// DESC : returns a HighScores object
// previously saved to %path
// USE : public
function HighScores::load(%path)
{
if(isObject(%path)) return; // call HighScores::load('path');
%obj = NewHighScores();
exec(%path);
%load = HighScoreSavedSet;
%obj.MaxCount = %load.getObject(0).MaxCount;
%load.remove(%load.getObject(0));
%obj.Scores = new SimSet();
for(%x=0;%x<%load.getCount();%x++)
{
%obj.Scores.add(%load.getObject(%x));
}
%load.clear();
HighScoreSavedSet.delete();
if(isObject(%load)) echo("%load is still an object");
if(isObject(HighScoreSavedSet)) echo("HighScoreSavedSet is still an object");
return %obj;
}

// FUNCTION: HighScores::copySimSet
// PARAMS :
// %set : set to copy
// DESC : returns a copy of %set
// USE : internal
function HighScores::copySimSet(%set)
{
if(!%set.IsMemberOfClass("SimSet")) return false;
%obj = new SimSet();
for(%x = 0; %x < %set.getCount(); %x++)
{
%obj.add(%set.getObject(%x));
}
return %obj;
}

// FUNCTION: HighScores::debug
// PARAMS : none
// DESC : echo's high score table to console
// USE : internal
function HighScores::debug(%this)
{
if(!isObject(%this.Scores))
{
echo("No Scores in Stack");
return;
}
for(%x=0;%x<%this.Scores.getCount();%x++)
{
echo(" Player: " @ %this.Scores.getObject(%x).PlayerName);
echo(" Scores: " @ %this.Scores.getObject(%x).PlayerScore);
echo(" ");
}
}




UPDATE: Remove the '%trap' condition identified by Aun - 2007-01-04@1800

Recent Blog Posts
List:11/19/07 - Cacheable Web Resources... Oh My!
09/29/07 - The Adventures of Coco the Gorrila in: CocoNuts
09/23/07 - How's it all Add Up?
07/11/07 - Ever wondered how to get your game project update to the rest of the team?
06/30/07 - The dog ate my homework, I swear!
06/20/07 - My latest news, and the new site I just launched ...
05/09/07 - $5,000 sound interesting?
05/01/07 - What Time is It?

Submit ResourceSubmit your own resources!

David Higgins   (Nov 24, 2006 at 20:14 GMT)
Oh, and for those curious as to how it all works together, here's my "test.cs" I used to test it's functionality.


function createRandomPlayerScore()
{
%player = new SimObject()
{
PlayerName = "MoZ";
PlayerScore = getRandom(0, 100);
};
return %player;
}

$highScores = NewHighScores();
$highScores.setMaximumScores(15);

for(%x = 0; %x < $highScores.getMaximumScores(); %x++)
{
%player = createRandomPlayerScore();
if($highScores.add(%player))
{
echo("Player has a new High Score");
}
}

function testSave()
{
$highscores.save("./TGBHighScores/data/saves/save.sav");
}

function testLoad()
{
return HighScores::load("~/data/saves/save.sav");
}

$highScores.debug();
testSave();
$highScores.delete();
echo(isObject($highScores));
$highScores = testLoad();


And yes, I realize, there's still some stuff to work on -- but I figured I'd get it out there, in the event I forget to work on it further or forget to post it when I finish it up -- if there appears to be interest in the object, I'll post periodic updates to it for added functionality, memory clean-up, etc.

Tom Bentz   (Nov 24, 2006 at 21:39 GMT)   Resource Rating: 5
Hey this is cool... I added a highscore functionality in my game as well. I hardcoded a maximum of 10 for the GUI screen but your code seems more dynamic. I might take up the task of creating a dynamic GUI screen to be able match this dynamic highscore code. Good job!

David Higgins   (Nov 25, 2006 at 00:21 GMT)
@Tom, the original idea behind the dynamic score count was so that anyone could just easily exec() the script and plug it into there own GUI and hard-code the 'setMaximumScores' line somewhere -- although, I am in the process of working on a GUI to go along with the script -- will probably use a combination of the Scroll and CtrlArray objects to hold onto everything so if the count is greater then the display can handle, it just allows you to scroll it --

Oliver Rendelmann - DerR   (Nov 25, 2006 at 08:21 GMT)   Resource Rating: 4
Um... what about that %this in "newHighScores()"? :)

David Higgins   (Nov 25, 2006 at 18:52 GMT)
@Oliver, yeah -- remnants of old code ... you can easily remove it.

It used to be:


function HighScores::create(%this)
{
// ...
};


Problem I ran into, and I've run into before, haha, just forgot about, is that you can't create an object with a class from within that class -- get errors about namespace unlinkage, etc, etc --

Aun Taraseina   (Jan 04, 2007 at 05:49 GMT)
Great resource !

alittle nit picking ,maybe change


%trap < 100

to

%trap++ < 100


Aun.

David Higgins   (Jan 05, 2007 at 00:56 GMT)
@Aun,

Actually, that %trap condition doesn't belong in the code -- the faults of using a langauge which does not requiring variable declarations is that when you add debug code and then remove it before a release, you sometimes miss things :)

%trap was simply just a counter I used to only sort through 100 objects, on high score tables that had more then 100 objects in them --

Thanks for pointing it out though ;)

Mark Newnam   (May 31, 2007 at 19:06 GMT)
David,

Is there a problem with this code in 1.5 Beta 3? I am using your class and test file (with the path modified for my directory) and it is not creating a save file. I don't receive any errors except when it tries to load the save file and can't find it.

Thanks,
Mark

David Higgins   (Jun 01, 2007 at 00:12 GMT)
Mark, I can't think of any reason this wouldn't work in TGB 1.5 Beta 3 ... though, I'm not sure ... I've not yet had a chance to really dig into 1.5 and do anything ... but I'm not aware of anything that has been changed that would break this code.

Perhaps your file path is incorrect?

Mark Newnam   (Jun 01, 2007 at 15:14 GMT)
Thanks for the reply David.. here are the load and save test functions that I'm using:

function testSave()
{
$highscores.save("~/data/save.sav");
}

function testLoad()
{
return HighScores::load("~/data/save.sav");
}

Maybe I'll try posting about it on the forums to see if anyone who's been playing around with 1.5 has an idea.. Thanks..

Jeremy Snyder   (Jun 21, 2007 at 18:29 GMT)   Resource Rating: 4
I like the style of the solution.

I did notice one leak, however:

At the end of the "HighScores::Sort"
you would want to delete the copied highscores object, %scores, after the while loop.
The objects inside are removed as they are added to the primary list, but this temporary SimSet object is left behind.

- Jeremy

David Higgins   (Jun 22, 2007 at 01:16 GMT)
@Jeremy -- thank you, and nice catch -- I'll update the resource now ;)

@Mark -- any luck?

Josiah Pisciotta   (Jul 18, 2007 at 21:16 GMT)
hey, i used this to create my own high score table that is a modified version of this... I was wondering how to plug this into the actual gui elemnt so that you can have a high score screen at the end of a level or the game or whatever that will show all the printed elements on it, got any suggestions?

David Higgins   (Jul 19, 2007 at 01:21 GMT)
Josiah,

You can create a GuiList control, or whatever it's called, and populate it through script before the GUI is displayed --

Check out the source to the GUI stuff in the common directory.




You must be a member and be logged in to either append comments or rate this resource.