1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2006-2015 Joerg Henrichs
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "race/highscores.hpp"
20
21 #include "io/utf_writer.hpp"
22 #include "io/xml_node.hpp"
23 #include "race/race_manager.hpp"
24 #include "utils/log.hpp"
25
26 #include <stdexcept>
27 #include <fstream>
28
29 // -----------------------------------------------------------------------------
Highscores(const HighscoreType & highscore_type,int num_karts,const RaceManager::Difficulty & difficulty,const std::string & track_name,const int number_of_laps,const bool reverse)30 Highscores::Highscores(const HighscoreType &highscore_type,
31 int num_karts,
32 const RaceManager::Difficulty &difficulty,
33 const std::string &track_name,
34 const int number_of_laps,
35 const bool reverse)
36 {
37 m_track = track_name;
38 m_highscore_type = highscore_type;
39 m_number_of_karts = num_karts;
40 m_difficulty = difficulty;
41 m_number_of_laps = number_of_laps;
42 m_reverse = reverse;
43
44 for(int i=0; i<HIGHSCORE_LEN; i++)
45 {
46 m_name[i] = "";
47 m_kart_name[i] = "";
48 m_time[i] = -9.9f;
49 }
50 }
51 // -----------------------------------------------------------------------------
Highscores(const XMLNode & node)52 Highscores::Highscores(const XMLNode &node)
53 {
54 m_track = "";
55 m_highscore_type = "HST_UNDEFINED";
56 m_number_of_karts = -1;
57 m_difficulty = -1;
58 m_number_of_laps = -1;
59 m_reverse = false;
60
61 for(int i=0; i<HIGHSCORE_LEN; i++)
62 {
63 m_name[i] = "";
64 m_kart_name[i] = "";
65 m_time[i] = -9.9f;
66 }
67
68 readEntry(node);
69 } // Highscores
70
71 // -----------------------------------------------------------------------------
readEntry(const XMLNode & node)72 void Highscores::readEntry(const XMLNode &node)
73 {
74 node.get("track-name", &m_track );
75 node.get("number-karts", &m_number_of_karts );
76 std::string hst="HST_UNDEFINED";
77 node.get("hscore-type", &hst );
78 m_highscore_type = (HighscoreType)hst;
79 node.get("difficulty", &m_difficulty );
80 node.get("number-of-laps", &m_number_of_laps );
81 node.get("reverse", &m_reverse );
82
83 for(unsigned int i=0; i<node.getNumNodes(); i++)
84 {
85 if (i >= HIGHSCORE_LEN)
86 {
87 Log::warn("Highscores", "Hiscore has too many entries.");
88 break;
89 }
90
91 const XMLNode *entry = node.getNode(i);
92 entry->get("time", &m_time[i] );
93 entry->getAndDecode("name", &m_name[i] );
94 entry->get("kartname", &m_kart_name[i] );
95
96 // a non-empty entry needs a non-empty kart name.
97 if (!(m_time[i] <= 0.0f || m_kart_name[i].size() > 0))
98 {
99 throw std::logic_error("Invalid highscore entry : empty kart name");
100 }
101 if (!(m_time[i] <= 0.0f || m_name[i].size() > 0))
102 {
103 throw std::logic_error("Invalid highscore entry : empty kart name");
104 }
105 }
106 } // readEntry
107
108 // -----------------------------------------------------------------------------
109 /** Writes the highscores in this entry to the writer. It will only write
110 * anything if there is actually a highscore recored (i.e. time >=0). Empty
111 * entries are created e.g. when changing the number of laps in the GUI,
112 * resulting in empty entries here.
113 * \param writer The file stream to write the data to.
114 */
writeEntry(UTFWriter & writer)115 void Highscores::writeEntry(UTFWriter &writer)
116 {
117 // Only
118 bool one_is_set = false;
119 for(unsigned int i=0; i<HIGHSCORE_LEN; i++)
120 one_is_set |= m_time[i]>=0;
121 if(!one_is_set) return;
122
123 writer << " <highscore track-name =\"" << m_track.c_str() << "\"\n";
124 writer << " number-karts =\"" << m_number_of_karts << "\"\n";
125 writer << " difficulty =\"" << m_difficulty << "\"\n";
126 writer << " hscore-type =\"" << m_highscore_type.c_str() << "\"\n";
127 writer << " number-of-laps=\"" << m_number_of_laps << "\"\n";
128 writer << " reverse =\"" << m_reverse << "\">\n";
129
130 for(int i=0; i<HIGHSCORE_LEN; i++)
131 {
132 if (m_time[i] > 0.0f)
133 {
134 assert(m_kart_name[i].size() > 0);
135 writer << " <entry time =\"" << m_time[i] << L"\"\n";
136 writer << " name =\"" << StringUtils::xmlEncode(m_name[i]) << L"\"\n";
137 writer << " kartname=\"" << m_kart_name[i]
138 << "\"/>\n";
139 }
140 } // for i
141 writer << " </highscore>\n";
142 } // writeEntry
143
144 // -----------------------------------------------------------------------------
matches(const HighscoreType & highscore_type,int num_karts,const RaceManager::Difficulty & difficulty,const std::string & track,const int number_of_laps,const bool reverse)145 int Highscores::matches(const HighscoreType &highscore_type,
146 int num_karts, const RaceManager::Difficulty &difficulty,
147 const std::string &track, const int number_of_laps,
148 const bool reverse)
149 {
150 return (m_highscore_type == highscore_type &&
151 m_track == track &&
152 m_difficulty == difficulty &&
153 m_number_of_laps == number_of_laps &&
154 m_number_of_karts == num_karts &&
155 m_reverse == reverse );
156 } // matches
157
158 // -----------------------------------------------------------------------------
159 /** Inserts the data into the highscore list.
160 * If the new entry is fast enough to
161 * be in the highscore list, the new position (1-HIGHSCORE_LEN) is returned,
162 * otherwise a 0.
163 */
addData(const std::string & kart_name,const core::stringw & name,const float time)164 int Highscores::addData(const std::string& kart_name,
165 const core::stringw& name, const float time)
166 {
167 int position=-1;
168 for(int i=0; i<HIGHSCORE_LEN; i++)
169 {
170 // Check for unused entry. If so, just insert the new record
171 if(m_time[i]<0.0f)
172 {
173 position=i;
174 break;
175 }
176 // Check if new entry is faster than than in slot 'i', if so
177 // move times etc and insert new entry
178 if(time < m_time[i])
179 {
180 for(int j=HIGHSCORE_LEN-2;j>=i;j--)
181 {
182 m_name[j+1] = m_name[j];
183 m_kart_name[j+1] = m_kart_name[j];
184 m_time[j+1] = m_time[j];
185 }
186 position = i;
187 break;
188 }
189 }//next score slot
190
191 if(position>=0)
192 {
193 m_track = RaceManager::get()->getTrackName();
194 m_number_of_karts = RaceManager::get()->getNumNonGhostKarts();
195 m_difficulty = RaceManager::get()->getDifficulty();
196 m_number_of_laps = RaceManager::get()->getNumLaps();
197 m_reverse = RaceManager::get()->getReverseTrack();
198 m_name[position] = name;
199 m_time[position] = time;
200 m_kart_name[position] = kart_name;
201 }
202
203 return position+1;
204 } // addData
205
206 // -----------------------------------------------------------------------------
getNumberEntries() const207 int Highscores::getNumberEntries() const
208 {
209 for(int i=HIGHSCORE_LEN-1; i>=0; i--)
210 {
211 if(m_time[i]>0) return i+1;
212 }
213 return 0;
214 } // getNumberEntries
215
216 // -----------------------------------------------------------------------------
getEntry(int number,std::string & kart_name,core::stringw & name,float * const time) const217 void Highscores::getEntry(int number, std::string &kart_name,
218 core::stringw &name, float *const time) const
219 {
220 if(number<0 || number>getNumberEntries())
221 {
222 Log::warn("Highscores", "Accessing undefined highscore entry:");
223 Log::warn("Highscores", "Number %d, but %d entries are defined.", number,
224 getNumberEntries());
225 Log::warn("Highscores", "This error can be ignored, but no highscores are available.");
226 return;
227 }
228 kart_name = m_kart_name[number];
229 name = m_name[number];
230 *time = m_time[number];
231
232 } // getEntry
233
234 // -----------------------------------------------------------------------------
235