diff --git a/src/company_gui.cpp b/src/company_gui.cpp index aee2105377..dd474b9044 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1976,6 +1976,8 @@ struct CompanyInfrastructureWindow : Window size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + WidgetDimensions::scaled.hsep_indent); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + WidgetDimensions::scaled.hsep_indent); + size->width += padding.width; + uint total_height = ((rail_lines + road_lines + tram_lines + 2 + 3) * FONT_HEIGHT_NORMAL) + (4 * EXP_SPACING); /* Set height of the total line. */ @@ -2476,18 +2478,19 @@ struct CompanyWindow : Window case WID_C_DESC_VEHICLE_COUNTS: SetDParamMaxValue(0, 5000); // Maximum number of vehicles for (uint i = 0; i < lengthof(_company_view_vehicle_count_strings); i++) { - size->width = std::max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width); + size->width = std::max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width + padding.width); } break; case WID_C_DESC_INFRASTRUCTURE_COUNTS: SetDParamMaxValue(0, UINT_MAX); - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width); + size->width = GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width; size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_WATER).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_STATION).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_NONE).width); + size->width += padding.width; break; case WID_C_DESC_OWNERS: { @@ -2506,12 +2509,13 @@ struct CompanyWindow : Window case WID_C_VIEW_INFRASTRUCTURE: case WID_C_COMPANY_PASSWORD: case WID_C_COMPANY_JOIN: - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width); + size->width = GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width; size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_BUILD_HQ_BUTTON).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_RELOCATE_HQ).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_PASSWORD).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width); + size->width += padding.width; break; diff --git a/src/core/math_func.hpp b/src/core/math_func.hpp index 701eaaafad..a73daa477d 100644 --- a/src/core/math_func.hpp +++ b/src/core/math_func.hpp @@ -93,7 +93,7 @@ static inline T Clamp(const T a, const T min, const T max) * is returned otherwise the border of the interval is returned, according * which side of the interval was 'left'. * - * @note If the min value is greater than the return value is the average of the min and max. + * @note If the min value is greater than the max, return value is the average of the min and max. * @param a The value to clamp/truncate. * @param min The minimum of the interval. * @param max the maximum of the interval. @@ -102,7 +102,10 @@ static inline T Clamp(const T a, const T min, const T max) template static inline T SoftClamp(const T a, const T min, const T max) { - if (min > max) return (min + max) / 2; + if (min > max) { + using U = std::make_unsigned_t; + return min - (U(min) - max) / 2; + } if (a <= min) return min; if (a >= max) return max; return a; diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 478818c200..de7a865247 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -1576,7 +1576,7 @@ struct GenerateProgressWindow : public Window { case WID_GP_PROGRESS_TEXT: for (uint i = 0; i < GWP_CLASS_COUNT; i++) { - size->width = std::max(size->width, GetStringBoundingBox(_generation_class_table[i]).width); + size->width = std::max(size->width, GetStringBoundingBox(_generation_class_table[i]).width + padding.width); } size->height = FONT_HEIGHT_NORMAL * 2 + WidgetDimensions::scaled.vsep_normal; break; diff --git a/src/highscore.cpp b/src/highscore.cpp index 69ca393a91..220f8df9e1 100644 --- a/src/highscore.cpp +++ b/src/highscore.cpp @@ -12,6 +12,7 @@ #include "company_base.h" #include "company_func.h" #include "cheat_func.h" +#include "fileio_func.h" #include "string_func.h" #include "strings_func.h" #include "table/strings.h" @@ -19,7 +20,7 @@ #include "safeguards.h" -HighScore _highscore_table[SP_HIGHSCORE_END][5]; ///< various difficulty-settings; top 5 +HighScoresTable _highscore_table; ///< Table with all the high scores. std::string _highscore_file; ///< The file to store the highscore data in. static const StringID _endgame_perf_titles[] = { @@ -48,31 +49,34 @@ StringID EndGameGetPerformanceTitleFromValue(uint value) return _endgame_perf_titles[value]; } -/** Save the highscore for the company */ -int8 SaveHighScoreValue(const Company *c) +/** + * Save the highscore for the company + * @param c The company to insert. + * @return The index the company got in the high score table, or -1 when it did not end up in the table. + */ +int8_t SaveHighScoreValue(const Company *c) { - HighScore *hs = _highscore_table[SP_CUSTOM]; - uint i; - uint16 score = c->old_economy[0].performance_history; - /* Exclude cheaters from the honour of being in the highscore table */ if (CheatHasBeenUsed()) return -1; - for (i = 0; i < lengthof(_highscore_table[0]); i++) { - /* You are in the TOP5. Move all values one down and save us there */ - if (hs[i].score <= score) { - /* move all elements one down starting from the replaced one */ - memmove(&hs[i + 1], &hs[i], sizeof(HighScore) * (lengthof(_highscore_table[0]) - i - 1)); - SetDParam(0, c->index); - SetDParam(1, c->index); - GetString(hs[i].company, STR_HIGHSCORE_NAME, lastof(hs[i].company)); // get manager/company name string - hs[i].score = score; - hs[i].title = EndGameGetPerformanceTitleFromValue(score); - return i; - } - } + auto &highscores = _highscore_table[SP_CUSTOM]; + uint16 score = c->old_economy[0].performance_history; - return -1; // too bad; we did not make it into the top5 + auto it = std::find_if(highscores.begin(), highscores.end(), [&score](auto &highscore) { return highscore.score <= score; }); + + /* If we cannot find it, our score is not high enough. */ + if (it == highscores.end()) return -1; + + /* Move all elements one down starting from the replaced one */ + std::move_backward(it, highscores.end() - 1, highscores.end()); + + /* Fill the elements. */ + SetDParam(0, c->index); + SetDParam(1, c->index); + it->name = GetString(STR_HIGHSCORE_NAME); // get manager/company name string + it->score = score; + it->title = EndGameGetPerformanceTitleFromValue(score); + return std::distance(highscores.begin(), it); } /** Sort all companies given their performance */ @@ -85,95 +89,82 @@ static bool HighScoreSorter(const Company * const &a, const Company * const &b) * Save the highscores in a network game when it has ended * @return Position of the local company in the highscore list. */ -int8 SaveHighScoreValueNetwork() +int8_t SaveHighScoreValueNetwork() { const Company *cl[MAX_COMPANIES]; - uint count = 0; - int8 company = -1; + size_t count = 0; + int8_t local_company_place = -1; /* Sort all active companies with the highest score first */ for (const Company *c : Company::Iterate()) cl[count++] = c; std::sort(std::begin(cl), std::begin(cl) + count, HighScoreSorter); - { - uint i; + /* Clear the high scores from the previous network game. */ + auto &highscores = _highscore_table[SP_MULTIPLAYER]; + std::fill(highscores.begin(), highscores.end(), HighScore{}); - memset(_highscore_table[SP_MULTIPLAYER], 0, sizeof(_highscore_table[SP_MULTIPLAYER])); + for (size_t i = 0; i < count && i < highscores.size(); i++) { + const Company *c = cl[i]; + auto &highscore = highscores[i]; + SetDParam(0, c->index); + SetDParam(1, c->index); + highscore.name = GetString(STR_HIGHSCORE_NAME); // get manager/company name string + highscore.score = c->old_economy[0].performance_history; + highscore.title = EndGameGetPerformanceTitleFromValue(highscore.score); - /* Copy over Top5 companies */ - for (i = 0; i < lengthof(_highscore_table[SP_MULTIPLAYER]) && i < count; i++) { - HighScore *hs = &_highscore_table[SP_MULTIPLAYER][i]; - - SetDParam(0, cl[i]->index); - SetDParam(1, cl[i]->index); - GetString(hs->company, STR_HIGHSCORE_NAME, lastof(hs->company)); // get manager/company name string - hs->score = cl[i]->old_economy[0].performance_history; - hs->title = EndGameGetPerformanceTitleFromValue(hs->score); - - /* get the ranking of the local company */ - if (cl[i]->index == _local_company) company = i; - } + if (c->index == _local_company) local_company_place = static_cast(i); } - /* Add top5 companies to highscore table */ - return company; + return local_company_place; } /** Save HighScore table to file */ void SaveToHighScore() { - FILE *fp = fopen(_highscore_file.c_str(), "wb"); + std::unique_ptr fp(fopen(_highscore_file.c_str(), "wb")); + if (fp == nullptr) return; - if (fp != nullptr) { - uint i; - HighScore *hs; - - for (i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { - for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { - /* First character is a command character, so strlen will fail on that */ - byte length = ClampTo(std::min(sizeof(hs->company), StrEmpty(hs->company) ? 0 : strlen(&hs->company[1]) + 1)); - - if (fwrite(&length, sizeof(length), 1, fp) != 1 || // write away string length - fwrite(hs->company, length, 1, fp) > 1 || // Yes... could be 0 bytes too - fwrite(&hs->score, sizeof(hs->score), 1, fp) != 1 || - fwrite(" ", 2, 1, fp) != 1) { // XXX - placeholder for hs->title, not saved anymore; compatibility - DEBUG(misc, 1, "Could not save highscore."); - i = SP_SAVED_HIGHSCORE_END; - break; - } + /* Does not iterate through the complete array!. */ + for (int i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { + for (HighScore &hs : _highscore_table[i]) { + /* This code is weird and old fashioned to keep compatibility with the old high score files. */ + byte name_length = ClampTo(hs.name.size()); + if (fwrite(&name_length, sizeof(name_length), 1, fp.get()) != 1 || // Write the string length of the name + fwrite(hs.name.data(), name_length, 1, fp.get()) > 1 || // Yes... could be 0 bytes too + fwrite(&hs.score, sizeof(hs.score), 1, fp.get()) != 1 || + fwrite(" ", 2, 1, fp.get()) != 1) { // Used to be hs.title, not saved anymore; compatibility + DEBUG(misc, 1, "Could not save highscore."); + return; } } - fclose(fp); } } /** Initialize the highscore table to 0 and if any file exists, load in values */ void LoadFromHighScore() { - FILE *fp = fopen(_highscore_file.c_str(), "rb"); + std::fill(_highscore_table.begin(), _highscore_table.end(), HighScores{}); - memset(_highscore_table, 0, sizeof(_highscore_table)); + std::unique_ptr fp(fopen(_highscore_file.c_str(), "rb")); + if (fp == nullptr) return; - if (fp != nullptr) { - uint i; - HighScore *hs; + /* Does not iterate through the complete array!. */ + for (int i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { + for (HighScore &hs : _highscore_table[i]) { + /* This code is weird and old fashioned to keep compatibility with the old high score files. */ + byte name_length; + char buffer[std::numeric_limits::max() + 1]; - for (i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { - for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { - byte length; - if (fread(&length, sizeof(length), 1, fp) != 1 || - fread(hs->company, std::min(lengthof(hs->company), length), 1, fp) > 1 || // Yes... could be 0 bytes too - fread(&hs->score, sizeof(hs->score), 1, fp) != 1 || - fseek(fp, 2, SEEK_CUR) == -1) { // XXX - placeholder for hs->title, not saved anymore; compatibility - DEBUG(misc, 1, "Highscore corrupted"); - i = SP_SAVED_HIGHSCORE_END; - break; - } - StrMakeValidInPlace(hs->company, lastof(hs->company), SVS_NONE); - hs->title = EndGameGetPerformanceTitleFromValue(hs->score); + if (fread(&name_length, sizeof(name_length), 1, fp.get()) != 1 || + fread(buffer, name_length, 1, fp.get()) > 1 || // Yes... could be 0 bytes too + fread(&hs.score, sizeof(hs.score), 1, fp.get()) != 1 || + fseek(fp.get(), 2, SEEK_CUR) == -1) { // Used to be hs.title, not saved anymore; compatibility + DEBUG(misc, 1, "Highscore corrupted"); + return; } + hs.name = StrMakeValid(std::string_view(buffer, name_length)); + hs.title = EndGameGetPerformanceTitleFromValue(hs.score); } - fclose(fp); } } diff --git a/src/highscore.h b/src/highscore.h index 8993e83afa..3fb5f4a817 100644 --- a/src/highscore.h +++ b/src/highscore.h @@ -15,17 +15,14 @@ #include "settings_type.h" struct HighScore { - /** - * The name of the company and president. - * The + 5 is for the comma and space or possibly other characters - * that join the two names in this single string and the '\0'. - */ - char company[(MAX_LENGTH_COMPANY_NAME_CHARS + MAX_LENGTH_PRESIDENT_NAME_CHARS + 5) * MAX_CHAR_LENGTH]; - StringID title; ///< NOSAVE, has troubles with changing string-numbers. - uint16 score; ///< The score for this high score. Do NOT change type, will break hs.dat + std::string name; ///< The name of the companyy and president. + StringID title = INVALID_STRING_ID; ///< NOSAVE, has troubles with changing string-numbers. + uint16 score = 0; ///< The score for this high score. Do NOT change type, will break hs.dat }; -extern HighScore _highscore_table[SP_HIGHSCORE_END][5]; +using HighScores = std::array; ///< Record 5 high scores +using HighScoresTable = std::array; ///< Record high score for each of the difficulty levels +extern HighScoresTable _highscore_table; void SaveToHighScore(); void LoadFromHighScore(); diff --git a/src/highscore_gui.cpp b/src/highscore_gui.cpp index 6165f4570a..2982e70824 100644 --- a/src/highscore_gui.cpp +++ b/src/highscore_gui.cpp @@ -178,7 +178,7 @@ struct HighScoreWindow : EndGameHighScoreBaseWindow { void OnPaint() override { - const HighScore *hs = _highscore_table[this->window_number]; + const auto &hs = _highscore_table[this->window_number]; this->SetupHighScoreEndWindow(); Point pt = this->GetTopLeft(ScaleSpriteTrad(640), ScaleSpriteTrad(480)); @@ -187,14 +187,14 @@ struct HighScoreWindow : EndGameHighScoreBaseWindow { DrawStringMultiLine(pt.x + ScaleSpriteTrad(70), pt.x + ScaleSpriteTrad(570), pt.y, pt.y + ScaleSpriteTrad(140), !_networking ? STR_HIGHSCORE_TOP_COMPANIES_WHO_REACHED : STR_HIGHSCORE_TOP_COMPANIES_NETWORK_GAME, TC_FROMSTRING, SA_CENTER); /* Draw Highscore peepz */ - for (uint8 i = 0; i < lengthof(_highscore_table[0]); i++) { + for (uint8_t i = 0; i < ClampTo(hs.size()); i++) { SetDParam(0, i + 1); DrawString(pt.x + ScaleSpriteTrad(40), pt.x + ScaleSpriteTrad(600), pt.y + ScaleSpriteTrad(140 + i * 55), STR_HIGHSCORE_POSITION); - if (hs[i].company[0] != '\0') { + if (!hs[i].name.empty()) { TextColour colour = (this->rank == i) ? TC_RED : TC_BLACK; // draw new highscore in red - SetDParamStr(0, hs[i].company); + SetDParamStr(0, hs[i].name); DrawString(pt.x + ScaleSpriteTrad(71), pt.x + ScaleSpriteTrad(569), pt.y + ScaleSpriteTrad(140 + i * 55), STR_JUST_BIG_RAW_STRING, colour); SetDParam(0, hs[i].title); SetDParam(1, hs[i].score); diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index fec5869590..b1e8390dcc 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -754,9 +754,9 @@ struct TooltipsWindow : public Window /* Correctly position the tooltip position, watch out for window and cursor size * Clamp value to below main toolbar and above statusbar. If tooltip would * go below window, flip it so it is shown above the cursor */ - pt.y = Clamp(_cursor.pos.y + _cursor.total_size.y + _cursor.total_offs.y + 5, scr_top, scr_bot); + pt.y = SoftClamp(_cursor.pos.y + _cursor.total_size.y + _cursor.total_offs.y + 5, scr_top, scr_bot); if (pt.y + sm_height > scr_bot) pt.y = std::min(_cursor.pos.y + _cursor.total_offs.y - 5, scr_bot) - sm_height; - pt.x = sm_width >= _screen.width ? 0 : Clamp(_cursor.pos.x - (sm_width >> 1), 0, _screen.width - sm_width); + pt.x = sm_width >= _screen.width ? 0 : SoftClamp(_cursor.pos.x - (sm_width >> 1), 0, _screen.width - sm_width); return pt; } diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index cc58bae678..bc3d3dd7b7 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -2258,7 +2258,7 @@ struct ScanProgressWindow : public Window { SetDParamMaxDigits(1, 4); /* We really don't know the width. We could determine it by scanning the NewGRFs, * but this is the status window for scanning them... */ - size->width = std::max(size->width, GetStringBoundingBox(STR_NEWGRF_SCAN_STATUS).width); + size->width = std::max(size->width, GetStringBoundingBox(STR_NEWGRF_SCAN_STATUS).width + padding.width); size->height = FONT_HEIGHT_NORMAL * 2 + WidgetDimensions::scaled.vsep_normal; break; } diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 34d1d3525b..16a5cde9e5 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1565,8 +1565,6 @@ public: _settings_client.gui.station_numtracks = widget - WID_BRAS_PLATFORM_NUM_BEGIN; _settings_client.gui.station_dragdrop = false; - _settings_client.gui.station_dragdrop = false; - const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : nullptr; if (statspec != nullptr && HasBit(statspec->disallowed_lengths, _settings_client.gui.station_platlength - 1)) { /* The previously selected number of platforms in invalid */ @@ -1600,8 +1598,6 @@ public: _settings_client.gui.station_platlength = widget - WID_BRAS_PLATFORM_LEN_BEGIN; _settings_client.gui.station_dragdrop = false; - _settings_client.gui.station_dragdrop = false; - const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : nullptr; if (statspec != nullptr && HasBit(statspec->disallowed_platforms, _settings_client.gui.station_numtracks - 1)) { /* The previously selected number of tracks in invalid */ diff --git a/src/script/api/script_tilelist.hpp b/src/script/api/script_tilelist.hpp index 5d2c9d1898..ea584b7111 100644 --- a/src/script/api/script_tilelist.hpp +++ b/src/script/api/script_tilelist.hpp @@ -37,7 +37,7 @@ public: void AddTile(TileIndex tile); /** - * Remove the tiles inside the rectangle between tile_from and tile_to form the list. + * Remove the tiles inside the rectangle between tile_from and tile_to from the list. * @param tile_from One corner of the tiles to remove. * @param tile_to The other corner of the files to remove. * @pre ScriptMap::IsValidTile(tile_from). diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 6e0ea281c0..fb2df4995e 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2623,7 +2623,7 @@ struct GameSettingsWindow : Window { }; for (uint i = 0; i < lengthof(setting_types); i++) { SetDParam(0, setting_types[i]); - size->width = std::max(size->width, GetStringBoundingBox(STR_CONFIG_SETTING_TYPE).width); + size->width = std::max(size->width, GetStringBoundingBox(STR_CONFIG_SETTING_TYPE).width + padding.width); } size->height = 2 * FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal + std::max(size->height, GetSettingsTree().GetMaxHelpHeight(size->width)); diff --git a/src/tests/math_func.cpp b/src/tests/math_func.cpp index 0e58b79e42..45667b9012 100644 --- a/src/tests/math_func.cpp +++ b/src/tests/math_func.cpp @@ -75,3 +75,58 @@ TEST_CASE("IntSqrtTest - FindSqRt") CHECK(9 == IntSqrt(88)); CHECK(1696 == IntSqrt(2876278)); } + + +TEST_CASE("ClampTo") +{ + CHECK(0 == ClampTo(std::numeric_limits::lowest())); + CHECK(0 == ClampTo(-1)); + CHECK(0 == ClampTo(0)); + CHECK(1 == ClampTo(1)); + + CHECK(255 == ClampTo(std::numeric_limits::max())); + CHECK(255 == ClampTo(256)); + CHECK(255 == ClampTo(255)); + CHECK(254 == ClampTo(254)); + + CHECK(-128 == ClampTo(std::numeric_limits::lowest())); + CHECK(-128 == ClampTo(-129)); + CHECK(-128 == ClampTo(-128)); + CHECK(-127 == ClampTo(-127)); + + CHECK(127 == ClampTo(std::numeric_limits::max())); + CHECK(127 == ClampTo(128)); + CHECK(127 == ClampTo(127)); + CHECK(126 == ClampTo(126)); + + CHECK(126 == ClampTo(static_cast(126))); + CHECK(126 == ClampTo(static_cast(126))); + CHECK(0 == ClampTo(static_cast(-126))); + CHECK(0 == ClampTo(static_cast(-126))); + + /* The realm around 64 bits types is tricky as there is not one type/method that works for all. */ + + /* lowest/max uint64_t does not get clamped when clamping to uint64_t. */ + CHECK(std::numeric_limits::lowest() == ClampTo(std::numeric_limits::lowest())); + CHECK(std::numeric_limits::max() == ClampTo(std::numeric_limits::max())); + + /* negative int64_t get clamped to 0. */ + CHECK(0 == ClampTo(std::numeric_limits::lowest())); + CHECK(0 == ClampTo(int64_t(-1))); + /* positive int64_t remain the same. */ + CHECK(1 == ClampTo(int64_t(1))); + CHECK(static_cast(std::numeric_limits::max()) == ClampTo(std::numeric_limits::max())); + + /* max uint64_t gets clamped to max int64_t. */ + CHECK(std::numeric_limits::max() == ClampTo(std::numeric_limits::max())); +} + + +TEST_CASE("SoftClamp") +{ + /* Special behaviour of soft clamp returning the average of min/max when min is higher than max. */ + CHECK(1250 == SoftClamp(0, 1500, 1000)); + int million = 1000 * 1000; + CHECK(1250 * million == SoftClamp(0, 1500 * million, 1000 * million)); + CHECK(0 == SoftClamp(0, 1500 * million, -1500 * million)); +} diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 63e27c3d31..a0895f7aa8 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -365,7 +365,7 @@ public: size->height = (5 + SETTING_OVERRIDE_COUNT) * FONT_HEIGHT_NORMAL + padding.height; size->width = GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTIONS_TITLE).width; for (uint i = 0; i < TACT_COUNT; i++ ) { - size->width = std::max(size->width, GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN + i).width); + size->width = std::max(size->width, GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN + i).width + padding.width); } size->width += padding.width; break; diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 2379889658..f733a82213 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -214,8 +214,13 @@ bool VideoDriver_Win32Base::MakeWindow(bool full_screen, bool resize) if (this->main_wnd != nullptr) { if (!_window_maximize && resize) SetWindowPos(this->main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE); } else { - int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; - int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; + /* Center on the workspace of the primary display. */ + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi); + + int x = (mi.rcWork.right - mi.rcWork.left - w) / 2; + int y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2; char window_title[64]; seprintf(window_title, lastof(window_title), "OpenTTD %s", _openttd_revision); diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index f3209e7d2b..4d18c83c8c 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -421,11 +421,8 @@ void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, int button scroll = true; uint avg_height = height / (uint)list.size(); - /* Check at least there is space for one item. */ - assert(available_height >= avg_height); - - /* Fit the list. */ - uint rows = available_height / avg_height; + /* Fit the list; create at least one row, even if there is no height available. */ + uint rows = std::max(available_height / avg_height, 1); height = rows * avg_height; /* Add space for the scrollbar. */