@@ -26,7 +26,7 @@ namespace devilution {
2626
2727namespace {
2828
29- struct ExperienceData {
29+ class ExperienceData {
3030 /* * Specifies the experience point limit of each level. The given values are defaults used if the data file is missing. */
3131 std::vector<uint32_t > levelThresholds {
3232 0 ,
@@ -82,12 +82,32 @@ struct ExperienceData {
8282 1583495809
8383 };
8484
85- static constexpr uint8_t DefaultMaxLevel = 50 ;
86- uint8_t maxLevel = DefaultMaxLevel;
85+ public:
86+ uint8_t getMaxLevel () const
87+ {
88+ if (levelThresholds.empty ())
89+ return 0 ;
90+ return static_cast <uint8_t >(std::min<size_t >(levelThresholds.size () - 1 , std::numeric_limits<uint8_t >::max ()));
91+ }
92+
93+ DVL_REINITIALIZES void clear ()
94+ {
95+ levelThresholds.clear ();
96+ }
8797
88- [[nodiscard]] uint32_t getThresholdForLevel (unsigned level)
98+ [[nodiscard]] uint32_t getThresholdForLevel (unsigned level) const
8999 {
90- return levelThresholds[std::min<size_t >({ level, maxLevel, levelThresholds.size () - 1 })];
100+ return levelThresholds[std::min<size_t >(level, getMaxLevel ())];
101+ }
102+
103+ void setThresholdForLevel (unsigned level, uint32_t experience)
104+ {
105+ if (level >= levelThresholds.size ()) {
106+ // To avoid ValidatePlayer() resetting players to 0 experience we need to use the maximum possible value here
107+ // As long as the file has no gaps it'll get initialised properly.
108+ levelThresholds.resize (static_cast <size_t >(level) + 1 , std::numeric_limits<uint32_t >::max ());
109+ }
110+ levelThresholds[level] = experience;
91111 }
92112} ExperienceData;
93113
@@ -190,12 +210,11 @@ void ReloadExperienceData()
190210 return ;
191211 }
192212
193- ExperienceData.levelThresholds .clear ();
194- bool foundMaxLevelRecord = false ;
213+ ExperienceData.clear ();
195214 do {
196215 uint8_t level = 0 ;
197216 uint32_t experience = 0 ;
198- bool isMaxLevelRecord = false ;
217+ bool skipRecord = false ;
199218 for (auto &column : columns) {
200219 result = DiscardMultipleFields (result.next , dataFile.end (), column.skipLength );
201220
@@ -208,11 +227,11 @@ void ReloadExperienceData()
208227 switch (column.type ) {
209228 case ExperienceColumnDefinition::ColumnType::Level: {
210229 auto fromCharsResult = std::from_chars (result.next , dataFile.end (), level);
211- if (fromCharsResult.ec == std::errc::invalid_argument && !foundMaxLevelRecord ) {
230+ if (fromCharsResult.ec == std::errc::invalid_argument) {
212231 // not a signless numeric value, is this the MaxLevel line?
213232 result = GetNextField (fromCharsResult.ptr , dataFile.end ());
214233 if (result.value == " MaxLevel" ) {
215- isMaxLevelRecord = true ;
234+ skipRecord = true ;
216235 }
217236 // else it was an invalid value, TODO: let the player know the data file contains errors
218237 } else {
@@ -238,28 +257,16 @@ void ReloadExperienceData()
238257 default :
239258 result = DiscardField (result.next , dataFile.end ());
240259 }
241- }
242260
243- if (isMaxLevelRecord) {
244- ExperienceData.maxLevel = experience == 0 ? ExperienceData::DefaultMaxLevel
245- : static_cast <uint8_t >(std::min<uint32_t >(experience, std::numeric_limits<uint8_t >::max ()));
246- foundMaxLevelRecord = true ;
247- } else {
248- if (foundMaxLevelRecord && level > ExperienceData.maxLevel ) {
249- // ignore values we will never use. Should we notify the player?
250- } else {
251- if (level >= ExperienceData.levelThresholds .size ()) {
252- // To avoid ValidatePlayer() resetting players to 0 experience we need to use the maximum possible value here
253- // As long as the file has no gaps it'll get initialised properly.
254- ExperienceData.levelThresholds .resize (static_cast <size_t >(level) + 1 , std::numeric_limits<uint32_t >::max ());
255- }
256- ExperienceData.levelThresholds [level] = experience;
257- }
261+ if (skipRecord)
262+ break ;
258263 }
259264
260- if (!result.endOfRecord ()) {
265+ if (!skipRecord)
266+ ExperienceData.setThresholdForLevel (level, experience);
267+
268+ if (!result.endOfRecord ())
261269 result = DiscardRemainingFields (result.next , dataFile.end ());
262- }
263270 } while (!result.endOfFile ());
264271}
265272
@@ -277,7 +284,7 @@ uint32_t GetNextExperienceThresholdForLevel(unsigned level)
277284
278285uint8_t GetMaximumCharacterLevel ()
279286{
280- return ExperienceData.maxLevel ;
287+ return ExperienceData.getMaxLevel () ;
281288}
282289
283290const _sfx_id herosounds[enum_size<HeroClass>::value][enum_size<HeroSpeech>::value] = {
0 commit comments