2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD 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.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file highscore.cpp Definition of functions used for highscore handling */
11 #include "highscore.h"
12 #include "company_base.h"
13 #include "company_func.h"
14 #include "cheat_func.h"
15 #include "string_func.h"
16 #include "strings_func.h"
17 #include "table/strings.h"
20 #include "safeguards.h"
22 HighScore _highscore_table
[SP_HIGHSCORE_END
][5]; ///< various difficulty-settings; top 5
23 std::string _highscore_file
; ///< The file to store the highscore data in.
25 static const StringID _endgame_perf_titles
[] = {
26 STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN
,
27 STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN
,
28 STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN
,
29 STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN
,
30 STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN
,
31 STR_HIGHSCORE_PERFORMANCE_TITLE_ENTREPRENEUR
,
32 STR_HIGHSCORE_PERFORMANCE_TITLE_ENTREPRENEUR
,
33 STR_HIGHSCORE_PERFORMANCE_TITLE_INDUSTRIALIST
,
34 STR_HIGHSCORE_PERFORMANCE_TITLE_INDUSTRIALIST
,
35 STR_HIGHSCORE_PERFORMANCE_TITLE_CAPITALIST
,
36 STR_HIGHSCORE_PERFORMANCE_TITLE_CAPITALIST
,
37 STR_HIGHSCORE_PERFORMANCE_TITLE_MAGNATE
,
38 STR_HIGHSCORE_PERFORMANCE_TITLE_MAGNATE
,
39 STR_HIGHSCORE_PERFORMANCE_TITLE_MOGUL
,
40 STR_HIGHSCORE_PERFORMANCE_TITLE_MOGUL
,
41 STR_HIGHSCORE_PERFORMANCE_TITLE_TYCOON_OF_THE_CENTURY
44 StringID
EndGameGetPerformanceTitleFromValue(uint value
)
46 value
= std::min
<uint
>(value
/ 64, lengthof(_endgame_perf_titles
) - 1);
48 return _endgame_perf_titles
[value
];
51 /** Save the highscore for the company */
52 int8
SaveHighScoreValue(const Company
*c
)
54 HighScore
*hs
= _highscore_table
[SP_CUSTOM
];
56 uint16 score
= c
->old_economy
[0].performance_history
;
58 /* Exclude cheaters from the honour of being in the highscore table */
59 if (CheatHasBeenUsed()) return -1;
61 for (i
= 0; i
< lengthof(_highscore_table
[0]); i
++) {
62 /* You are in the TOP5. Move all values one down and save us there */
63 if (hs
[i
].score
<= score
) {
64 /* move all elements one down starting from the replaced one */
65 memmove(&hs
[i
+ 1], &hs
[i
], sizeof(HighScore
) * (lengthof(_highscore_table
[0]) - i
- 1));
66 SetDParam(0, c
->index
);
67 SetDParam(1, c
->index
);
68 GetString(hs
[i
].company
, STR_HIGHSCORE_NAME
, lastof(hs
[i
].company
)); // get manager/company name string
70 hs
[i
].title
= EndGameGetPerformanceTitleFromValue(score
);
75 return -1; // too bad; we did not make it into the top5
78 /** Sort all companies given their performance */
79 static bool HighScoreSorter(const Company
* const &a
, const Company
* const &b
)
81 return b
->old_economy
[0].performance_history
< a
->old_economy
[0].performance_history
;
85 * Save the highscores in a network game when it has ended
86 * @return Position of the local company in the highscore list.
88 int8
SaveHighScoreValueNetwork()
90 const Company
*cl
[MAX_COMPANIES
];
94 /* Sort all active companies with the highest score first */
95 for (const Company
*c
: Company::Iterate()) cl
[count
++] = c
;
97 std::sort(std::begin(cl
), std::begin(cl
) + count
, HighScoreSorter
);
102 memset(_highscore_table
[SP_MULTIPLAYER
], 0, sizeof(_highscore_table
[SP_MULTIPLAYER
]));
104 /* Copy over Top5 companies */
105 for (i
= 0; i
< lengthof(_highscore_table
[SP_MULTIPLAYER
]) && i
< count
; i
++) {
106 HighScore
*hs
= &_highscore_table
[SP_MULTIPLAYER
][i
];
108 SetDParam(0, cl
[i
]->index
);
109 SetDParam(1, cl
[i
]->index
);
110 GetString(hs
->company
, STR_HIGHSCORE_NAME
, lastof(hs
->company
)); // get manager/company name string
111 hs
->score
= cl
[i
]->old_economy
[0].performance_history
;
112 hs
->title
= EndGameGetPerformanceTitleFromValue(hs
->score
);
114 /* get the ranking of the local company */
115 if (cl
[i
]->index
== _local_company
) company
= i
;
119 /* Add top5 companies to highscore table */
123 /** Save HighScore table to file */
124 void SaveToHighScore()
126 FILE *fp
= fopen(_highscore_file
.c_str(), "wb");
132 for (i
= 0; i
< SP_SAVED_HIGHSCORE_END
; i
++) {
133 for (hs
= _highscore_table
[i
]; hs
!= endof(_highscore_table
[i
]); hs
++) {
134 /* First character is a command character, so strlen will fail on that */
135 byte length
= std::min(sizeof(hs
->company
), StrEmpty(hs
->company
) ? 0 : strlen(&hs
->company
[1]) + 1);
137 if (fwrite(&length
, sizeof(length
), 1, fp
) != 1 || // write away string length
138 fwrite(hs
->company
, length
, 1, fp
) > 1 || // Yes... could be 0 bytes too
139 fwrite(&hs
->score
, sizeof(hs
->score
), 1, fp
) != 1 ||
140 fwrite(" ", 2, 1, fp
) != 1) { // XXX - placeholder for hs->title, not saved anymore; compatibility
141 Debug(misc
, 1, "Could not save highscore.");
142 i
= SP_SAVED_HIGHSCORE_END
;
151 /** Initialize the highscore table to 0 and if any file exists, load in values */
152 void LoadFromHighScore()
154 FILE *fp
= fopen(_highscore_file
.c_str(), "rb");
156 memset(_highscore_table
, 0, sizeof(_highscore_table
));
162 for (i
= 0; i
< SP_SAVED_HIGHSCORE_END
; i
++) {
163 for (hs
= _highscore_table
[i
]; hs
!= endof(_highscore_table
[i
]); hs
++) {
165 if (fread(&length
, sizeof(length
), 1, fp
) != 1 ||
166 fread(hs
->company
, std::min
<int>(lengthof(hs
->company
), length
), 1, fp
) > 1 || // Yes... could be 0 bytes too
167 fread(&hs
->score
, sizeof(hs
->score
), 1, fp
) != 1 ||
168 fseek(fp
, 2, SEEK_CUR
) == -1) { // XXX - placeholder for hs->title, not saved anymore; compatibility
169 Debug(misc
, 1, "Highscore corrupted");
170 i
= SP_SAVED_HIGHSCORE_END
;
173 StrMakeValidInPlace(hs
->company
, lastof(hs
->company
), SVS_NONE
);
174 hs
->title
= EndGameGetPerformanceTitleFromValue(hs
->score
);