Greetings all. Today I will be introducing you to the BZFlag feature called Ratio Tracking. If you are a BZFlag player and have seen me online, you may have noticed in my motto something like this: Ratio: 316-247 (1.28) [Commander]. What this is, is my Ratio Tracking feature for BZFlag at work! By adding this feature into BZFlag, you will be able to keep track of your kills to deaths! You will also be able to disable the tracking or displaying of it in your motto at anytime! You get a cool ranking to go with it too! Let us begin:
You will first have to get the source code from the BZFlag SVN trunk. You can do so with the following command:
svn co https://bzflag.svn.sourceforge.net/svnroot/bzflag/trunk/bzflag
The source code will be downloaded to your computer. It is the latest version from trunk that is stable.
You will be modifying four files. These four files will be:
./src/bzflag/LocalPlayer.cxx
./src/bzflag/GUIOptionsMenu.cxx
./src/bzflag/JoinMenu.cxx
./src/bzflag/MainMenu.cxx
LocalPlayer.cxx: We are modifying this file to add in the tracker for a kill or death when it occurs. WARNING! This is a very critical file. DO NOT modify anything else in this file, for you will ruin gameplay.
Search (Ctrl+F) for Player::changeScore(deltaWins, deltaLosses, deltaTks);
Add after that:
if (BZDB.isTrue("killerratio")) {
FILE*data;
char temp[75];
int kills;
int deaths;
kills=0;
deaths=0;
data=fopen("stats","r");
if (data) {
fgets(temp,75,data);
kills=atoi(temp);
fgets(temp,75,data);
deaths=atoi(temp);
fclose(data);
}
data=fopen("stats","w");
if (data) {
kills+=deltaWins;
deaths+=deltaLosses;
fprintf(data,"%dn%dn",kills,deaths);
fclose(data);
}
}
What we are doing here is adding to the changeScore function. We do it here because when the score changes, so does our kills and deaths. The deltaWins and deltaLosses variables tell us if we gain points from kills or lose points from deaths. You can also include deltaTKs if you want, but I did not since that may not accurately reflect the value. Note that we added a new BZDB variable called “killerratio” which is whether or not this feature is enabled to begin with. Finally we write it to a file. I realize you could easily modify the file later on if you felt like it, but come on, that would not be fun. Better to see how good you are and actually gain points than just rig the darn thing.
Moving on to the next file.
GUIOptionsMenu.cxx:
Search for option->setLabel("Motto Display Length:");
Skip down 5 lines until after the listHUD.push_back(option); part.
Add after that:
// Ratio Tracking
option = new HUDuiList;
option->setFontFace(fontFace);
option->setLabel("Ratio Tracking:");
option->setCallback(callback, (void*)"V");
options = &option->getList();
options->push_back(std::string("Off"));
options->push_back(std::string("On"));
option->update();
listHUD.push_back(option);
// Ratio Tracking in Motto
option = new HUDuiList;
option->setFontFace(fontFace);
option->setLabel("Display Ratio in Motto:");
option->setCallback(callback, (void*)"W");
options = &option->getList();
options->push_back(std::string("Off"));
options->push_back(std::string("On"));
option->update();
listHUD.push_back(option);
What we do here is add two options to the GUI Settings of the Options Menu. Those are Ratio Tracking and Display Ratio in Motto. We also give them unique identifiers, V and W. I just chose those since they were not used. Finally, they have two settings, Off and On.
But there is more to do in this file.
Search for ((HUDuiList*)listHUD[i++])->setIndex((int)BZDB.eval("mottoDispLen") / 4); Add after that:
((HUDuiList*)listHUD[i++])->setIndex(BZDB.isTrue("killerratio") ? 1
: 0);
((HUDuiList*)listHUD[i++])->setIndex(BZDB.isTrue("displayratio") ? 1
: 0);
These will setup the link between the menu options and the BZDB variables. We have two variables, killerratio and displayratio. killerratio like we already talking about, can enable or disable the tracking completely. displayratio is whether or not to display it in the motto. As the GUI option we added above states.
Finally, we have to add a few more lines.
Find case 'E': Skip down three lines until after the break; part.
Add after that:
case 'V':
{
BZDB.set("killerratio", list->getIndex() ? "1" : "0");
break;
}
case 'W':
{
BZDB.set("displayratio", list->getIndex() ? "1" : "0");
break;
}
What this does is we are looking at which option identifier we are at, and then setting it, either to 1 or 0 for on or off.
Next file:
JoinMenu.cxx
Find // set fields
Skip 10 lines until you pass the port->setString(buffer); part.
Add after that:
if (BZDB.isTrue("displayratio")) {
FILE*data;
char temp[75];
char finalstring[255];
char rank[50];
int kills;
int deaths;
kills=0;
deaths=0;
data=fopen("stats","r");
if (data) {
fgets(temp,75,data);
kills=atoi(temp);
fgets(temp,75,data);
deaths=atoi(temp);
fclose(data);
}
strcpy(finalstring,"");
if ((double)kills/(double)deaths>=0.005) {strcpy(rank,"Bullet Magnet");} else {strcpy(rank,"Just Sucks");}
if ((double)kills/(double)deaths>=0.01) {strcpy(rank,"Failure");}
if ((double)kills/(double)deaths>=0.05) {strcpy(rank,"Nothing");}
if ((double)kills/(double)deaths>=0.15) {strcpy(rank,"Servant");}
if ((double)kills/(double)deaths>=0.25) {strcpy(rank,"Recruit");}
if ((double)kills/(double)deaths>=0.35) {strcpy(rank,"Apprentice");}
if ((double)kills/(double)deaths>=0.45) {strcpy(rank,"Private");}
if ((double)kills/(double)deaths>=0.55) {strcpy(rank,"Sergeant");}
if ((double)kills/(double)deaths>=0.75) {strcpy(rank,"Lieutenant");}
if ((double)kills/(double)deaths>=1.00) {strcpy(rank,"Captain");}
if ((double)kills/(double)deaths>=1.10) {strcpy(rank,"Major");}
if ((double)kills/(double)deaths>=1.20) {strcpy(rank,"Commander");}
if ((double)kills/(double)deaths>=1.35) {strcpy(rank,"Colonel");}
if ((double)kills/(double)deaths>=1.50) {strcpy(rank,"Brigadier");}
if ((double)kills/(double)deaths>=1.65) {strcpy(rank,"General");}
if ((double)kills/(double)deaths>=1.80) {strcpy(rank,"Semi-Pro");}
if ((double)kills/(double)deaths>=2.00) {strcpy(rank,"Pro");}
if ((double)kills/(double)deaths>=2.15) {strcpy(rank,"1337");}
if ((double)kills/(double)deaths>=2.25) {strcpy(rank,"BZ-Freak");}
if ((double)kills/(double)deaths>=2.50) {strcpy(rank,"Godly");}
if ((double)kills/(double)deaths>=2.60) {strcpy(rank,"Master");}
if ((double)kills/(double)deaths>=2.75) {strcpy(rank,"Grand Master");}
if ((double)kills/(double)deaths>=3.00) {strcpy(rank,"Unstoppable");}
if ((double)kills/(double)deaths>=3.50) {strcpy(rank,"Untouchable");}
if ((double)kills/(double)deaths>=4.00) {strcpy(rank,"Invincible");}
if ((double)kills/(double)deaths>=15.00) {strcpy(rank,"Asian");}
if (kills<=150 && deaths<=150) {strcpy(rank,"Pending");}
sprintf(finalstring,"Ratio: %d-%d (%.2lf) [%s]",kills,deaths,(double)kills/(double)deaths,rank);
motto->setString(finalstring);
}
This looks big, but is not too hard to understand. First, we check if we wanted to display our ratio in our motto. If we did, it then reads our file and sets it. Next, we see what our rank is based on our ratio. Note that you can have fun with the names and change them if you really want to. Those just seem fitting. Finally, the ratio is not very accurate until you get a good amount of kills. We check to see if kills or deaths is less than 150. If one of those is less than 150, your rank will be “Pending” until you reach that amount in kills or deaths. This is so that we can get more accurate results on a ranking. The last part displays the string with 2 integers, a float, and a string, respectively. Ratio: Kills-Deaths (Ratio) [Rank]. And finally we set that as our motto.
Our last file.
MainMenu.cxx
Find } else if (_focus == quit) { Add after that:
// save resources
dumpResources();
if (alternateConfig == "")
CFGMGR.write(getCurrentConfigFileName());
else
CFGMGR.write(alternateConfig);
This is a simple “lazy” hack for saving our configuration when we exit. Because pressing Quit Game leads to a different function, we would have to modify that itself and a few more things. Doing what Save Settings does here solves this easily. If you look just above, the code is the same for Save Settings.
So that is it! You will now run the following commands:
./autogen.sh
./configure
make
sudo make install
If everything worked out right, you can now type bzflag and start up your client. One note to make: The stats file is created in whichever directory you run the bzflag command. I personally created a launcher on my desktop to run the command bzflag. This starts it up in your home directory by default, since that is where terminal always starts in. This can provide flexibility but be a problem at the same time. One, you could have a multitude of score profiles, based on map style if you really wanted. You can also copy and paste the file around to always have your stats. But it’s up to you. I personally just run it in one place, all the time for consistent stats.
By default, the feature is disabled. Start up BZFlag and go to Options -> GUI Settings. At the bottom you will find two new options. You can enable one, or both. I recommend both are either on or off all the time. If you turn off Display Ratio in Motto then you can delete whatever is there and change it and it will not be forced to change by the game. If it is on, every time you enter the Join Game screen, you will see it updates, so as you change maps or rejoin, it will update!
Enjoy! I make harmless additions to BZFlag for the fun of it. You will at no time at all see any modifications to the client code that can potentially alter or destroy gameplay in an unfair manner. By following the instructions above, you will successfully be adding a feature to the client that I personally like. This code is current as of the trunk on September 27, 2011. This post may be updated or reposted when newer versions and changes to the source code are made. Have fun showing off your ratio and trying to rise up in the ranks! (Or falling.)
Too lazy to update the files manually? You can download the files modified in this tutorial below:
http://sigonasr2.servegame.org/uploader/multi-file-uploader/uploads/LocalPlayer.cxx
http://sigonasr2.servegame.org/uploader/multi-file-uploader/uploads/GUIOptionsMenu.cxx
http://sigonasr2.servegame.org/uploader/multi-file-uploader/uploads/JoinMenu.cxx
http://sigonasr2.servegame.org/uploader/multi-file-uploader/uploads/MainMenu.cxx
Finally, screenshots of it in action (Click to view detail):

Settings in the GUI Options.

Ratio before joining a server.

After a few kills and deaths.