diff --git a/src/lang/english.txt b/src/lang/english.txt index 39dea02a7e..8ce08f7e4a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1305,6 +1305,8 @@ STR_CONFIG_SETTING_SHOW_TRAIN_LENGTH_IN_DETAILS :Show train leng STR_CONFIG_SETTING_SHOW_TRAIN_LENGTH_IN_DETAILS_HELPTEXT :Show train length in the vehicle details window STR_CONFIG_SETTING_SHOW_VEHICLE_GROUP_IN_DETAILS :Show vehicle group in details: {STRING2} STR_CONFIG_SETTING_SHOW_VEHICLE_GROUP_IN_DETAILS_HELPTEXT :Show vehicle group name in the vehicle details window +STR_CONFIG_SETTING_SHOW_TRAIN_WEIGHT_RATIOS_IN_DETAILS :Show train weight ratios in details: {STRING2} +STR_CONFIG_SETTING_SHOW_TRAIN_WEIGHT_RATIOS_IN_DETAILS_HELPTEXT :Show train weight ratios in the vehicle details window STR_CONFIG_SETTING_SHOW_RESTRICTED_SIG_DEF :Show restricted electric signals using default graphics: {STRING2} STR_CONFIG_SETTING_SHOW_RESTRICTED_SIG_DEF_HELPTEXT :Show electric signals with routing restriction programs using the default signal graphics with a blue signal post, instead of using any NewGRF signal graphics. This is to make it easier to visually distinguish restricted signals. @@ -2548,8 +2550,8 @@ STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED :{STRING} {STRIN STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_WEIGHT :{STRING} {STRING} {STRING} {WEIGHT_SHORT} then STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_POWER :{STRING} {STRING} {STRING} {POWER} then STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_FORCE :{STRING} {STRING} {STRING} {FORCE} then -STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_POWER_WEIGHT_RATIO :{STRING} {STRING} {STRING} {POWER} / {STRING1} then -STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_FORCE_WEIGHT_RATIO :{STRING} {STRING} {STRING} {FORCE} / {STRING1} then +STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_POWER_WEIGHT_RATIO :{STRING} {STRING} {STRING} {POWER_WEIGHT_RATIO} then +STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_FORCE_WEIGHT_RATIO :{STRING} {STRING} {STRING} {FORCE_WEIGHT_RATIO} then STR_TRACE_RESTRICT_CONDITIONAL_ORDER_STATION :{STRING} {STRING} {STRING} {STATION} then STR_TRACE_RESTRICT_CONDITIONAL_ORDER_WAYPOINT :{STRING} {STRING} {STRING} {WAYPOINT} then STR_TRACE_RESTRICT_CONDITIONAL_ORDER_DEPOT :{STRING} {STRING} {STRING} {DEPOT} then @@ -2614,7 +2616,7 @@ STR_TRACE_RESTRICT_INSTRUCTION_LIST_TOOLTIP :{BLACK}Click an STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM :{WHITE}Can't insert instruction STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM :{WHITE}Can't modify instruction STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM :{WHITE}Can't remove instruction -STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE :{WHITE}Value too large, maximum is {COMMA} +STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE :{WHITE}Value too large, maximum is {DECIMAL} STR_TRACE_RESTRICT_ERROR_NO_PROGRAM :No trace restrict program exists STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE :Offset too large STR_TRACE_RESTRICT_ERROR_CAN_T_CHANGE_CONDITIONALITY :Can't change conditionality @@ -4169,6 +4171,7 @@ STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}Weight: STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}Weight: {LTBLUE}{WEIGHT_SHORT} {BLACK}Power: {LTBLUE}{POWER}{BLACK} Max. speed: {LTBLUE}{VELOCITY} {BLACK}Max. T.E.: {LTBLUE}{FORCE} STR_VEHICLE_INFO_TRAIN_LENGTH :{BLACK}Train length {LTBLUE}{DECIMAL} tile{P "" s} {STRING4} +STR_VEHICLE_INFO_WEIGHT_RATIOS :{BLACK}Power / weight: {LTBLUE}{POWER_WEIGHT_RATIO} {BLACK} Max. T.E / weight: {LTBLUE}{FORCE_WEIGHT_RATIO} STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR :{BLACK}Profit this year: {LTBLUE}{CURRENCY_LONG} (last year: {CURRENCY_LONG}) STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_LIFETIME :{STRING2} (lifetime: {CURRENCY_LONG}) @@ -5532,6 +5535,7 @@ STR_JUST_CURRENCY_SHORT :{CURRENCY_SHORT STR_JUST_CURRENCY_LONG :{CURRENCY_LONG} STR_JUST_CARGO_LIST :{CARGO_LIST} STR_JUST_INT :{NUM} +STR_JUST_DECIMAL :{DECIMAL} STR_JUST_DATE_TINY :{DATE_TINY} STR_JUST_DATE_SHORT :{DATE_SHORT} STR_JUST_DATE_LONG :{DATE_LONG} @@ -5552,6 +5556,7 @@ STR_TINY_COMMA :{TINY_FONT}{COM STR_BLUE_COMMA :{BLUE}{COMMA} STR_RED_COMMA :{RED}{COMMA} STR_WHITE_COMMA :{WHITE}{COMMA} +STR_BLACK_DECIMAL :{BLACK}{DECIMAL} STR_TINY_BLACK_DECIMAL :{TINY_FONT}{BLACK}{DECIMAL} STR_COMPANY_MONEY :{WHITE}{CURRENCY_LONG} STR_BLACK_DATE_LONG :{BLACK}{DATE_LONG} diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 1d13978abc..a73602402e 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1593,6 +1593,7 @@ static SettingsContainer &GetSettingsTree() interface->Add(new SettingEntry("gui.timetable_arrival_departure")); interface->Add(new SettingEntry("gui.expenses_layout")); interface->Add(new SettingEntry("gui.show_train_length_in_details")); + interface->Add(new SettingEntry("gui.show_train_weight_ratios_in_details")); interface->Add(new SettingEntry("gui.show_vehicle_group_in_details")); interface->Add(new SettingEntry("gui.show_vehicle_list_company_colour")); } diff --git a/src/settings_type.h b/src/settings_type.h index 55a7bbdf9f..00d7efce34 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -168,6 +168,7 @@ struct GUISettings { byte missing_strings_threshold; ///< the number of missing strings before showing the warning uint8 graph_line_thickness; ///< the thickness of the lines in the various graph guis bool show_train_length_in_details; ///< show train length in vehicle details window top widget + bool show_train_weight_ratios_in_details; ///< show train weight ratios in vehicle details window top widget bool show_vehicle_group_in_details; ///< show vehicle group in vehicle details window top widget bool show_restricted_signal_default; ///< Show restricted electric signals using the default sprite uint8 osk_activation; ///< Mouse gesture to trigger the OSK. diff --git a/src/string.cpp b/src/string.cpp index 677152ef1e..fd3d8c6210 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -297,6 +297,54 @@ void str_strip_colours(char *str) *dst = '\0'; } +/** Scans the string for a wchar and replace it with another wchar + * @param str The string buffer + * @param last The pointer to the last element of the string buffer + * @param find The character to find + * @param replace The character to replace, may be 0 to not insert any character + * @return The pointer to the terminating null-character in the string buffer + */ +char *str_replace_wchar(char *str, const char *last, WChar find, WChar replace) +{ + char *dst = str; + + while (str <= last && *str != '\0') { + size_t len = Utf8EncodedCharLen(*str); + /* If the character is unknown, i.e. encoded length is 0 + * we assume worst case for the length check. + * The length check is needed to prevent Utf8Decode to read + * over the terminating '\0' if that happens to be placed + * within the encoding of an UTF8 character. */ + if ((len == 0 && str + 4 > last) || str + len > last) break; + + WChar c; + len = Utf8Decode(&c, str); + /* It's possible to encode the string termination character + * into a multiple bytes. This prevents those termination + * characters to be skipped */ + if (c == '\0') break; + + if (c != find) { + /* Copy the character back. Even if dst is current the same as str + * (i.e. no characters have been changed) this is quicker than + * moving the pointers ahead by len */ + if (dst + len > last) break; + do { + *dst++ = *str++; + } while (--len != 0); + } else { + str += len; + if (replace) { + len = Utf8EncodedCharLen(replace); + if (dst + len > last) break; + dst += Utf8Encode(dst, replace); + } + } + } + *dst = '\0'; + return dst; +} + /** * Get the length of an UTF-8 encoded string in number of characters * and thus not the number of bytes that the encoded string contains. @@ -343,9 +391,17 @@ bool strtolower(char *str) */ bool IsValidChar(WChar key, CharSetFilter afilter) { +#if !defined(STRGEN) && !defined(SETTINGSGEN) + extern WChar GetDecimalSeparatorChar(); +#endif switch (afilter) { case CS_ALPHANUMERAL: return IsPrintable(key); case CS_NUMERAL: return (key >= '0' && key <= '9'); +#if !defined(STRGEN) && !defined(SETTINGSGEN) + case CS_NUMERAL_DECIMAL: return (key >= '0' && key <= '9') || key == '.' || key == GetDecimalSeparatorChar(); +#else + case CS_NUMERAL_DECIMAL: return (key >= '0' && key <= '9') || key == '.'; +#endif case CS_NUMERAL_SPACE: return (key >= '0' && key <= '9') || key == ' '; case CS_ALPHA: return IsPrintable(key) && !(key >= '0' && key <= '9'); case CS_HEXADECIMAL: return (key >= '0' && key <= '9') || (key >= 'a' && key <= 'f') || (key >= 'A' && key <= 'F'); diff --git a/src/string_func.h b/src/string_func.h index 4bc401630b..981a83d747 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -46,6 +46,7 @@ void ValidateString(const char *str); void str_fix_scc_encoded(char *str, const char *last); void str_strip_colours(char *str); +char *str_replace_wchar(char *str, const char *last, WChar find, WChar replace); bool strtolower(char *str); bool StrValid(const char *str, const char *last); diff --git a/src/string_type.h b/src/string_type.h index 94d4304dfe..07fdba6fc1 100644 --- a/src/string_type.h +++ b/src/string_type.h @@ -26,6 +26,7 @@ enum CharSetFilter { CS_ALPHANUMERAL, ///< Both numeric and alphabetic and spaces and stuff CS_NUMERAL, ///< Only numeric ones + CS_NUMERAL_DECIMAL, ///< Only numeric and decimal separaters CS_NUMERAL_SPACE, ///< Only numbers and spaces CS_ALPHA, ///< Only alphabetic values CS_HEXADECIMAL, ///< Only hexadecimal characters diff --git a/src/strings.cpp b/src/strings.cpp index c9974a5cfa..2ca06bf67d 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -366,6 +366,15 @@ static char *FormatHexNumber(char *buff, uint64 number, const char *last) return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number); } +WChar GetDecimalSeparatorChar() +{ + WChar decimal_char = '.'; + const char *decimal_separator = _settings_game.locale.digit_decimal_separator; + if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator; + if (decimal_separator != NULL) Utf8Decode(&decimal_char, decimal_separator); + return decimal_char; +} + /** * Format a given number as a number of bytes with the SI prefix. * @param buff the buffer to write to @@ -844,6 +853,100 @@ uint ConvertDisplayForceToForce(uint force) return _units_force[_settings_game.locale.units_force].c.FromDisplay(force); } +static void ConvertWeightRatioToDisplay(const Units &unit, uint ratio, int64 &value, int64 &decimals) +{ + int64 input = ratio * 100; + decimals = 2; + if (_settings_game.locale.units_weight == 2) { + input *= 1000; + decimals += 3; + } + + const UnitConversion &weight_conv = _units_weight[_settings_game.locale.units_weight].c; + UnitConversion conv = unit.c; + conv.multiplier <<= weight_conv.shift; + + value = conv.ToDisplay(input) / (100 * weight_conv.multiplier); + + if (unit.c.multiplier >> unit.c.shift > 100) { + value /= 100; + decimals -= 2; + } +} + +static uint ConvertDisplayToWeightRatio(const Units &unit, double in) +{ + const UnitConversion &weight_conv = _units_weight[_settings_game.locale.units_weight].c; + UnitConversion conv = unit.c; + conv.multiplier <<= weight_conv.shift; + int64 multiplier = _settings_game.locale.units_weight == 2 ? 1000 : 1; + + return conv.FromDisplay(in * 100 * multiplier * weight_conv.multiplier, true, multiplier); +} + +static char *FormatUnitWeightRatio(char *buff, const char *last, const Units &unit, int64 raw_value) +{ + const char *unit_str = GetStringPtr(unit.s); + const char *weight_str = GetStringPtr(_units_weight[_settings_game.locale.units_weight].s); + + char tmp_buffer[32]; + strecpy(tmp_buffer, unit_str, lastof(tmp_buffer)); + char *insert_pt = str_replace_wchar(tmp_buffer, lastof(tmp_buffer), SCC_COMMA, SCC_DECIMAL); + strecpy(insert_pt, weight_str, lastof(tmp_buffer)); + str_replace_wchar(insert_pt, lastof(tmp_buffer), SCC_COMMA, '/'); + str_replace_wchar(insert_pt, lastof(tmp_buffer), 0xA0 /* NBSP */, 0); + + int64 value, decimals; + ConvertWeightRatioToDisplay(unit, raw_value, value, decimals); + + int64 args_array[2] = { value, decimals }; + StringParameters tmp_params(args_array); + buff = FormatString(buff, tmp_buffer, &tmp_params, last); + return buff; +} + +/** + * Convert the given internal power / weight ratio to the display decimal. + * @param ratio the power / weight ratio to convert + * @param value the output value + * @param decimals the output decimal offset + */ +void ConvertPowerWeightRatioToDisplay(uint ratio, int64 &value, int64 &decimals) +{ + ConvertWeightRatioToDisplay(_units_power[_settings_game.locale.units_power], ratio, value, decimals); +} + +/** + * Convert the given internal force / weight ratio to the display decimal. + * @param ratio the force / weight ratio to convert + * @param value the output value + * @param decimals the output decimal offset + */ +void ConvertForceWeightRatioToDisplay(uint ratio, int64 &value, int64 &decimals) +{ + ConvertWeightRatioToDisplay(_units_force[_settings_game.locale.units_force], ratio, value, decimals); +} + +/** + * Convert the given display value to the internal power / weight ratio. + * @param in the display value + * @return the converted power / weight ratio. + */ +uint ConvertDisplayToPowerWeightRatio(double in) +{ + return ConvertDisplayToWeightRatio(_units_power[_settings_game.locale.units_power], in); +} + +/** + * Convert the given display value to the internal force / weight ratio. + * @param in the display value + * @return the converted force / weight ratio. + */ +uint ConvertDisplayToForceWeightRatio(double in) +{ + return ConvertDisplayToWeightRatio(_units_force[_settings_game.locale.units_force], in); +} + /** * Parse most format codes within a string and write the result to a buffer. * @param buff The buffer to write the final string to. @@ -1391,6 +1494,22 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg break; } + case SCC_POWER_WEIGHT_RATIO: { // {POWER_WEIGHT_RATIO} + assert(_settings_game.locale.units_power < lengthof(_units_power)); + assert(_settings_game.locale.units_weight < lengthof(_units_weight)); + + buff = FormatUnitWeightRatio(buff, last, _units_power[_settings_game.locale.units_power], args->GetInt64()); + break; + } + + case SCC_FORCE_WEIGHT_RATIO: { // {FORCE_WEIGHT_RATIO} + assert(_settings_game.locale.units_force < lengthof(_units_force)); + assert(_settings_game.locale.units_weight < lengthof(_units_weight)); + + buff = FormatUnitWeightRatio(buff, last, _units_force[_settings_game.locale.units_force], args->GetInt64()); + break; + } + case SCC_COMPANY_NAME: { // {COMPANY} const Company *c = Company::GetIfValid(args->GetInt32()); if (c == NULL) break; diff --git a/src/strings_func.h b/src/strings_func.h index 2c7809d020..410414f600 100644 --- a/src/strings_func.h +++ b/src/strings_func.h @@ -139,6 +139,8 @@ uint ConvertDisplaySpeedToKmhishSpeed(uint speed); void InjectDParam(uint amount); +WChar GetDecimalSeparatorChar(); + /** * Set a string parameter \a v at index \a n in a given array \a s. * @param s Array of string parameters. diff --git a/src/table/control_codes.h b/src/table/control_codes.h index 7fc0a66155..cef2d77f05 100644 --- a/src/table/control_codes.h +++ b/src/table/control_codes.h @@ -62,6 +62,9 @@ enum StringControlCode { SCC_VELOCITY, SCC_HEIGHT, + SCC_POWER_WEIGHT_RATIO, + SCC_FORCE_WEIGHT_RATIO, + SCC_DATE_TINY, SCC_DATE_SHORT, SCC_DATE_LONG, diff --git a/src/table/settings.ini b/src/table/settings.ini index e740b4cbd5..29b68a4939 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -3910,6 +3910,15 @@ strhelp = STR_CONFIG_SETTING_SHOW_TRAIN_LENGTH_IN_DETAILS_HELPTEXT proc = RedrawScreen cat = SC_BASIC +[SDTC_BOOL] +var = gui.show_train_weight_ratios_in_details +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = false +str = STR_CONFIG_SETTING_SHOW_TRAIN_WEIGHT_RATIOS_IN_DETAILS +strhelp = STR_CONFIG_SETTING_SHOW_TRAIN_WEIGHT_RATIOS_IN_DETAILS_HELPTEXT +proc = RedrawScreen +cat = SC_EXPERT + [SDTC_BOOL] var = gui.show_vehicle_group_in_details flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC diff --git a/src/table/strgen_tables.h b/src/table/strgen_tables.h index bb3e7c2c7c..371c63cd04 100644 --- a/src/table/strgen_tables.h +++ b/src/table/strgen_tables.h @@ -83,6 +83,8 @@ static const CmdStruct _cmd_structs[] = { {"FORCE", EmitSingleChar, SCC_FORCE, 1, 0, C_NONE}, {"VELOCITY", EmitSingleChar, SCC_VELOCITY, 1, 0, C_NONE}, {"HEIGHT", EmitSingleChar, SCC_HEIGHT, 1, 0, C_NONE}, + {"POWER_WEIGHT_RATIO",EmitSingleChar, SCC_POWER_WEIGHT_RATIO, 1, 0, C_NONE}, + {"FORCE_WEIGHT_RATIO",EmitSingleChar, SCC_FORCE_WEIGHT_RATIO, 1, 0, C_NONE}, {"P", EmitPlural, 0, 0, -1, C_DONTCOUNT}, // plural specifier {"G", EmitGender, 0, 0, -1, C_DONTCOUNT}, // gender specifier diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index b908f7f4a7..f22f2fa3b4 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -58,6 +58,7 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_CONDFLAGS, TR_WIDGET_COMPARATOR, TR_WIDGET_VALUE_INT, + TR_WIDGET_VALUE_DECIMAL, TR_WIDGET_VALUE_DROPDOWN, TR_WIDGET_VALUE_DEST, TR_WIDGET_VALUE_SIGNAL, @@ -93,6 +94,7 @@ enum PanelWidgets { // Right DPR_VALUE_INT = 0, + DPR_VALUE_DECIMAL, DPR_VALUE_DROPDOWN, DPR_VALUE_DEST, DPR_VALUE_SIGNAL, @@ -479,6 +481,19 @@ static bool IsIntegerValueType(TraceRestrictValueType type) case TRVT_WEIGHT: case TRVT_POWER: case TRVT_FORCE: + return true; + + default: + return false; + } +} + +/** + * Return true if item type field @p type is a decimal value type + */ +static bool IsDecimalValueType(TraceRestrictValueType type) +{ + switch (type) { case TRVT_POWER_WEIGHT_RATIO: case TRVT_FORCE_WEIGHT_RATIO: return true; @@ -520,20 +535,47 @@ static uint ConvertIntegerValue(TraceRestrictValueType type, uint in, bool to_di : ConvertDisplayForceToForce(in); break; + case TRVT_PF_PENALTY: + return in; + + default: + NOT_REACHED(); + return 0; + } +} + +/** + * Convert integer values to decimal display units + */ +static void ConvertValueToDecimal(TraceRestrictValueType type, uint in, int64 &value, int64 &decimal) +{ + switch (type) { case TRVT_POWER_WEIGHT_RATIO: - return to_display - ? ConvertPowerToDisplayPower(in) * 10 - : ConvertDisplayPowerToPower(in) / 10; + ConvertPowerWeightRatioToDisplay(in, value, decimal); break; case TRVT_FORCE_WEIGHT_RATIO: - return to_display - ? ConvertForceToDisplayForce(in) * 10 - : ConvertDisplayForceToForce(in) / 10; + ConvertForceWeightRatioToDisplay(in, value, decimal); break; - case TRVT_PF_PENALTY: - return in; + default: + NOT_REACHED(); + } +} + +/** + * Convert decimal (double) display units to integer values + */ +static uint ConvertDecimalToValue(TraceRestrictValueType type, double in) +{ + switch (type) { + case TRVT_POWER_WEIGHT_RATIO: + return ConvertDisplayToPowerWeightRatio(in); + break; + + case TRVT_FORCE_WEIGHT_RATIO: + return ConvertDisplayToForceWeightRatio(in); + break; default: NOT_REACHED(); @@ -793,15 +835,11 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric case TRVT_POWER_WEIGHT_RATIO: instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_POWER_WEIGHT_RATIO; DrawInstructionStringConditionalIntegerCommon(item, properties); - SetDParam(4, STR_UNITS_WEIGHT_LONG_METRIC); - SetDParam(5, 100); break; case TRVT_FORCE_WEIGHT_RATIO: instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_FORCE_WEIGHT_RATIO; DrawInstructionStringConditionalIntegerCommon(item, properties); - SetDParam(4, STR_UNITS_WEIGHT_LONG_METRIC); - SetDParam(5, 100); break; default: @@ -1042,6 +1080,22 @@ public: break; } + case TR_WIDGET_VALUE_DECIMAL: { + TraceRestrictItem item = this->GetSelected(); + TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; + if (IsDecimalValueType(type)) { + int64 value, decimal; + ConvertValueToDecimal(type, GetTraceRestrictValue(item), value, decimal); + SetDParam(0, value); + SetDParam(1, decimal); + char *saved = _settings_game.locale.digit_group_separator; + _settings_game.locale.digit_group_separator = const_cast(""); + ShowQueryString(STR_JUST_DECIMAL, STR_TRACE_RESTRICT_VALUE_CAPTION, 16, this, CS_NUMERAL_DECIMAL, QSF_NONE); + _settings_game.locale.digit_group_separator = saved; + } + break; + } + case TR_WIDGET_VALUE_DROPDOWN: { TraceRestrictItem item = this->GetSelected(); switch (GetTraceRestrictTypeProperties(item).value_type) { @@ -1125,19 +1179,35 @@ public: TraceRestrictItem item = GetSelected(); TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; - if (!IsIntegerValueType(type) && type != TRVT_PF_PENALTY) { - return; - } + uint value; - uint value = ConvertIntegerValue(type, atoi(str), false); - if (value >= (1 << TRIFA_VALUE_COUNT)) { - SetDParam(0, ConvertIntegerValue(type, (1 << TRIFA_VALUE_COUNT) - 1, true)); - ShowErrorMessage(STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE, STR_EMPTY, WL_INFO); - return; - } + if (IsIntegerValueType(type) || type == TRVT_PF_PENALTY) { + value = ConvertIntegerValue(type, atoi(str), false); + if (value >= (1 << TRIFA_VALUE_COUNT)) { + SetDParam(0, ConvertIntegerValue(type, (1 << TRIFA_VALUE_COUNT) - 1, true)); + SetDParam(1, 0); + ShowErrorMessage(STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE, STR_EMPTY, WL_INFO); + return; + } - if (type == TRVT_PF_PENALTY) { - SetTraceRestrictAuxField(item, TRPPAF_VALUE); + if (type == TRVT_PF_PENALTY) { + SetTraceRestrictAuxField(item, TRPPAF_VALUE); + } + } else if (IsDecimalValueType(type)) { + char tmp_buffer[32]; + strecpy(tmp_buffer, str, lastof(tmp_buffer)); + str_replace_wchar(tmp_buffer, lastof(tmp_buffer), GetDecimalSeparatorChar(), '.'); + value = ConvertDecimalToValue(type, atof(tmp_buffer)); + if (value >= (1 << TRIFA_VALUE_COUNT)) { + int64 value, decimal; + ConvertValueToDecimal(type, (1 << TRIFA_VALUE_COUNT) - 1, value, decimal); + SetDParam(0, value); + SetDParam(1, decimal); + ShowErrorMessage(STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE, STR_EMPTY, WL_INFO); + return; + } + } else { + return; } SetTraceRestrictValue(item, value); @@ -1470,6 +1540,20 @@ public: break; } + case TR_WIDGET_VALUE_DECIMAL: { + SetDParam(0, 0); + SetDParam(1, 0); + TraceRestrictItem item = this->GetSelected(); + TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; + if (IsDecimalValueType(type)) { + int64 value, decimal; + ConvertValueToDecimal(type, GetTraceRestrictValue(item), value, decimal); + SetDParam(0, value); + SetDParam(1, decimal); + } + break; + } + case TR_WIDGET_CAPTION: { const TraceRestrictProgram *prog = this->GetProgram(); if (prog) { @@ -1629,6 +1713,7 @@ private: this->RaiseWidget(TR_WIDGET_CONDFLAGS); this->RaiseWidget(TR_WIDGET_COMPARATOR); this->RaiseWidget(TR_WIDGET_VALUE_INT); + this->RaiseWidget(TR_WIDGET_VALUE_DECIMAL); this->RaiseWidget(TR_WIDGET_VALUE_DROPDOWN); this->RaiseWidget(TR_WIDGET_VALUE_DEST); this->RaiseWidget(TR_WIDGET_VALUE_SIGNAL); @@ -1644,6 +1729,7 @@ private: this->DisableWidget(TR_WIDGET_CONDFLAGS); this->DisableWidget(TR_WIDGET_COMPARATOR); this->DisableWidget(TR_WIDGET_VALUE_INT); + this->DisableWidget(TR_WIDGET_VALUE_DECIMAL); this->DisableWidget(TR_WIDGET_VALUE_DROPDOWN); this->DisableWidget(TR_WIDGET_VALUE_DEST); this->DisableWidget(TR_WIDGET_VALUE_SIGNAL); @@ -1779,6 +1865,9 @@ private: if (IsIntegerValueType(properties.value_type)) { right_sel->SetDisplayedPlane(DPR_VALUE_INT); this->EnableWidget(TR_WIDGET_VALUE_INT); + } else if(IsDecimalValueType(properties.value_type)) { + right_sel->SetDisplayedPlane(DPR_VALUE_DECIMAL); + this->EnableWidget(TR_WIDGET_VALUE_DECIMAL); } else { switch (properties.value_type) { case TRVT_DENY: @@ -2021,6 +2110,8 @@ static const NWidgetPart _nested_program_widgets[] = { NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_RIGHT), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_INT), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_BLACK_COMMA, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_DECIMAL), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_BLACK_DECIMAL, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_VALUE_DROPDOWN), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_DEST), SetMinimalSize(124, 12), SetFill(1, 0), diff --git a/src/unit_conversion.h b/src/unit_conversion.h index 9dadc35872..581e21b897 100644 --- a/src/unit_conversion.h +++ b/src/unit_conversion.h @@ -17,3 +17,7 @@ uint ConvertPowerToDisplayPower(uint power); uint ConvertDisplayPowerToPower(uint power); uint ConvertForceToDisplayForce(uint force); uint ConvertDisplayForceToForce(uint force); +void ConvertPowerWeightRatioToDisplay(uint ratio, int64 &value, int64 &decimals); +void ConvertForceWeightRatioToDisplay(uint ratio, int64 &value, int64 &decimals); +uint ConvertDisplayToPowerWeightRatio(double in); +uint ConvertDisplayToForceWeightRatio(double in); diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 621c19b841..b228a50700 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -2049,6 +2049,7 @@ struct VehicleDetailsWindow : Window { TrainDetailsWindowTabs tab; ///< For train vehicles: which tab is displayed. Scrollbar *vscroll; bool vehicle_group_line_shown; + bool vehicle_weight_ratio_line_shown; /** Initialize a newly created vehicle details window */ VehicleDetailsWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) @@ -2130,6 +2131,11 @@ struct VehicleDetailsWindow : Window { return (_settings_client.gui.show_vehicle_group_in_details && v->group_id != INVALID_GROUP && v->group_id != DEFAULT_GROUP); } + bool ShouldShowWeightRatioLine(const Vehicle *v) const + { + return (v->type == VEH_TRAIN && _settings_client.gui.show_train_weight_ratios_in_details); + } + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { switch (widget) { @@ -2137,7 +2143,11 @@ struct VehicleDetailsWindow : Window { const Vehicle *v = Vehicle::Get(this->window_number); Dimension dim = { 0, 0 }; this->vehicle_group_line_shown = ShouldShowGroupLine(v); - size->height = WD_FRAMERECT_TOP + (this->vehicle_group_line_shown ? 5 : 4) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM; + this->vehicle_weight_ratio_line_shown = ShouldShowWeightRatioLine(v); + int lines = 4; + if (this->vehicle_group_line_shown) lines++; + if (this->vehicle_weight_ratio_line_shown) lines++; + size->height = WD_FRAMERECT_TOP + lines * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM; for (uint i = 0; i < 5; i++) SetDParamMaxValue(i, INT16_MAX); static const StringID info_strings[] = { @@ -2165,6 +2175,11 @@ struct VehicleDetailsWindow : Window { SetDParam(0, v->group_id); dim = maxdim(dim, GetStringBoundingBox(STR_VEHICLE_INFO_GROUP)); } + if (this->vehicle_weight_ratio_line_shown) { + SetDParamMaxValue(0, 1 << 16); + SetDParamMaxValue(1, 1 << 16); + dim = maxdim(dim, GetStringBoundingBox(STR_VEHICLE_INFO_WEIGHT_RATIOS)); + } SetDParam(0, STR_VEHICLE_INFO_AGE); dim = maxdim(dim, GetStringBoundingBox(STR_VEHICLE_INFO_AGE_RUNNING_COST_YR)); size->width = dim.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; @@ -2299,6 +2314,14 @@ struct VehicleDetailsWindow : Window { DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, string); y += FONT_HEIGHT_NORMAL; + bool should_show_weight_ratio = this->ShouldShowWeightRatioLine(v); + if (should_show_weight_ratio) { + SetDParam(0, (100 * Train::From(v)->gcache.cached_power) / max(1, Train::From(v)->gcache.cached_weight)); + SetDParam(1, (Train::From(v)->gcache.cached_max_te / 10) / max(1, Train::From(v)->gcache.cached_weight)); + DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_WEIGHT_RATIOS); + y += FONT_HEIGHT_NORMAL; + } + /* Draw profit */ if (v->type == VEH_TRAIN && _settings_client.gui.show_train_length_in_details) { const GroundVehicleCache *gcache = v->GetGroundVehicleCache(); @@ -2347,7 +2370,7 @@ struct VehicleDetailsWindow : Window { SetDParam(0, v->group_id); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_GROUP); } - if (this->vehicle_group_line_shown != should_show_group) { + if (this->vehicle_group_line_shown != should_show_group || this->vehicle_weight_ratio_line_shown != should_show_weight_ratio) { const_cast(this)->ReInit(); } break;