diff --git a/src/openrct2-ui/windows/Cheats.cpp b/src/openrct2-ui/windows/Cheats.cpp index b904bebaa9..81790fa998 100644 --- a/src/openrct2-ui/windows/Cheats.cpp +++ b/src/openrct2-ui/windows/Cheats.cpp @@ -356,7 +356,7 @@ class CheatsWindow final : public Window { private: char _moneySpinnerText[MONEY_STRING_MAXLENGTH]{}; - money32 _moneySpinnerValue = CHEATS_MONEY_DEFAULT; + money64 _moneySpinnerValue = CHEATS_MONEY_DEFAULT; int32_t _selectedStaffSpeed = 1; int32_t _parkRatingSpinnerValue{}; int32_t _yearSpinnerValue = 1; @@ -589,7 +589,7 @@ public: if (page == WINDOW_CHEATS_PAGE_MONEY && widgetIndex == WIDX_MONEY_SPINNER) { auto val = string_to_money(std::string(text).c_str()); - if (val != MONEY32_UNDEFINED) + if (val != MONEY64_UNDEFINED) { _moneySpinnerValue = val; } diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 2868bad3f0..7b65aa9db0 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -6423,14 +6423,13 @@ static void WindowRideIncomeDecreasePrimaryPrice(rct_window* w) WindowRideIncomeSetPrimaryPrice(w, price); } -static money16 WindowRideIncomeGetSecondaryPrice(rct_window* w) +static money64 WindowRideIncomeGetSecondaryPrice(rct_window* w) { auto ride = get_ride(w->rideId); if (ride == nullptr) return 0; - money16 price = ride->price[1]; - return price; + return ride->price[1]; } static void WindowRideIncomeSetSecondaryPrice(rct_window* w, money16 price) @@ -6457,7 +6456,7 @@ static bool WindowRideIncomeCanModifyPrimaryPrice(rct_window* w) */ static void WindowRideIncomeIncreaseSecondaryPrice(rct_window* w) { - money16 price = WindowRideIncomeGetSecondaryPrice(w); + auto price = WindowRideIncomeGetSecondaryPrice(w); if (price < 20.00_GBP) price++; @@ -6471,7 +6470,7 @@ static void WindowRideIncomeIncreaseSecondaryPrice(rct_window* w) */ static void WindowRideIncomeDecreaseSecondaryPrice(rct_window* w) { - money16 price = WindowRideIncomeGetSecondaryPrice(w); + auto price = WindowRideIncomeGetSecondaryPrice(w); if (price > 0.00_GBP) price--; @@ -6510,7 +6509,7 @@ static void WindowRideIncomeMouseup(rct_window* w, WidgetIndex widgetIndex) auto ride = get_ride(w->rideId); if (ride != nullptr) { - money_to_string(static_cast(ride->price[0]), _moneyInputText, MONEY_STRING_MAXLENGTH, true); + money_to_string(static_cast(ride->price[0]), _moneyInputText, MONEY_STRING_MAXLENGTH, true); WindowTextInputRawOpen( w, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, MONEY_STRING_MAXLENGTH); @@ -6522,9 +6521,9 @@ static void WindowRideIncomeMouseup(rct_window* w, WidgetIndex widgetIndex) break; case WIDX_SECONDARY_PRICE: { - money32 price32 = static_cast(WindowRideIncomeGetSecondaryPrice(w)); + auto price64 = WindowRideIncomeGetSecondaryPrice(w); - money_to_string(price32, _moneyInputText, MONEY_STRING_MAXLENGTH, true); + money_to_string(price64, _moneyInputText, MONEY_STRING_MAXLENGTH, true); WindowTextInputRawOpen( w, WIDX_SECONDARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, MONEY_STRING_MAXLENGTH); } @@ -6590,8 +6589,8 @@ static void WindowRideIncomeTextinput(rct_window* w, WidgetIndex widgetIndex, ch if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text == nullptr) return; - money32 price = string_to_money(text); - if (price == MONEY32_UNDEFINED) + money64 price = string_to_money(text); + if (price == MONEY64_UNDEFINED) { return; } diff --git a/src/openrct2/common.h b/src/openrct2/common.h index eb8e873922..d45be380d6 100644 --- a/src/openrct2/common.h +++ b/src/openrct2/common.h @@ -92,18 +92,17 @@ using money64 = fixed64_1dp; #define FIXED_1DP(whole, fraction) FIXED_XDP(1, whole, fraction) #define FIXED_2DP(whole, fraction) FIXED_XDP(10, whole, fraction) -// User defined literal to convert money literal to money32 -constexpr money32 operator"" _GBP(long double money) noexcept +constexpr money64 operator"" _GBP(long double money) noexcept { return money * 10; } -constexpr money32 ToMoney32FromGBP(int32_t money) noexcept +constexpr money64 ToMoney64FromGBP(int32_t money) noexcept { return money * 10; } -constexpr money32 ToMoney32FromGBP(double money) noexcept +constexpr money64 ToMoney64FromGBP(int64_t money) noexcept { return money * 10; } @@ -127,16 +126,6 @@ constexpr money64 ToMoney64(money16 value) return value == MONEY16_UNDEFINED ? MONEY64_UNDEFINED : value; } -constexpr money32 ToMoney32(money64 value) -{ - return value == MONEY64_UNDEFINED ? MONEY32_UNDEFINED : static_cast(value); -} - -constexpr money16 ToMoney16(money64 value) -{ - return value == MONEY64_UNDEFINED ? MONEY16_UNDEFINED : static_cast(value); -} - using StringId = uint16_t; #define SafeFree(x) \ diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index da695bd6b5..33dcbae35f 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -781,7 +781,7 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) else if (argv[0] == "scenario_initial_cash" && invalidArguments(&invalidArgs, int_valid[0])) { auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::InitialCash, std::clamp(ToMoney32FromGBP(int_val[0]), 0.00_GBP, 1000000.00_GBP)); + ScenarioSetSetting::InitialCash, std::clamp(ToMoney64FromGBP(int_val[0]), 0.00_GBP, 1000000.00_GBP)); scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) { if (res->Error != GameActions::Status::Ok) console.WriteLineError("set scenario_initial_cash command failed, likely due to permissions."); @@ -792,10 +792,9 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) } else if (argv[0] == "current_loan" && invalidArguments(&invalidArgs, int_valid[0])) { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::InitialLoan, - std::clamp( - ToMoney64FromGBP(int_val[0]) - ToMoney64FromGBP(int_val[0] % 1000), 0.00_GBP, gMaxBankLoan)); + auto amount = std::clamp( + ToMoney64FromGBP(int_val[0]) - ToMoney64FromGBP(int_val[0] % 1000), 0.00_GBP, gMaxBankLoan); + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::InitialLoan, amount); scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) { if (res->Error != GameActions::Status::Ok) console.WriteLineError("set current_loan command failed, likely due to permissions."); @@ -806,9 +805,9 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) } else if (argv[0] == "max_loan" && invalidArguments(&invalidArgs, int_valid[0])) { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::MaximumLoanSize, - std::clamp(ToMoney32FromGBP(int_val[0]) - ToMoney32FromGBP(int_val[0] % 1000), 0.00_GBP, 5000000.00_GBP)); + auto amount = std::clamp( + ToMoney64FromGBP(int_val[0]) - ToMoney64FromGBP(int_val[0] % 1000), 0.00_GBP, 5000000.00_GBP); + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::MaximumLoanSize, amount); scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) { if (res->Error != GameActions::Status::Ok) console.WriteLineError("set max_loan command failed, likely due to permissions."); @@ -820,7 +819,7 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) else if (argv[0] == "guest_initial_cash" && invalidArguments(&invalidArgs, double_valid[0])) { auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::AverageCashPerGuest, std::clamp(ToMoney32FromGBP(double_val[0]), 0.00_GBP, 1000.00_GBP)); + ScenarioSetSetting::AverageCashPerGuest, std::clamp(ToMoney64FromGBP(double_val[0]), 0.00_GBP, 1000.00_GBP)); scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) { if (res->Error != GameActions::Status::Ok) console.WriteLineError("set guest_initial_cash command failed, likely due to permissions."); @@ -984,7 +983,7 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) else if (argv[0] == "land_rights_cost" && invalidArguments(&invalidArgs, double_valid[0])) { auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::CostToBuyLand, std::clamp(ToMoney32FromGBP(double_val[0]), 0.00_GBP, 200.00_GBP)); + ScenarioSetSetting::CostToBuyLand, std::clamp(ToMoney64FromGBP(double_val[0]), 0.00_GBP, 200.00_GBP)); scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) { if (res->Error != GameActions::Status::Ok) console.WriteLineError("set land_rights_cost command failed, likely due to permissions."); @@ -997,7 +996,7 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) { auto scenarioSetSetting = ScenarioSetSettingAction( ScenarioSetSetting::CostToBuyConstructionRights, - std::clamp(ToMoney32FromGBP(double_val[0]), 0.00_GBP, 200.00_GBP)); + std::clamp(ToMoney64FromGBP(double_val[0]), 0.00_GBP, 200.00_GBP)); scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) { if (res->Error != GameActions::Status::Ok) console.WriteLineError("set construction_rights_cost command failed, likely due to permissions."); diff --git a/src/openrct2/localisation/Localisation.cpp b/src/openrct2/localisation/Localisation.cpp index 2c11088c96..c0b7b6df20 100644 --- a/src/openrct2/localisation/Localisation.cpp +++ b/src/openrct2/localisation/Localisation.cpp @@ -421,7 +421,7 @@ void format_readable_speed(char* buf, size_t bufSize, uint64_t sizeBytes) format_string(buf, bufSize, STR_NETWORK_SPEED_SEC, args); } -money32 string_to_money(const char* string_to_monetise) +money64 string_to_money(const char* string_to_monetise) { const char* decimal_char = language_get_string(STR_LOCALE_DECIMAL_POINT); const currency_descriptor* currencyDesc = &CurrencyDescriptors[EnumValue(gConfigGeneral.CurrencyFormat)]; @@ -445,7 +445,7 @@ money32 string_to_money(const char* string_to_monetise) else if (*src_ptr == decimal_char[0]) { if (hasDecSep) - return MONEY32_UNDEFINED; + return MONEY64_UNDEFINED; hasDecSep = true; // Replace localised decimal separator with an English one. @@ -456,7 +456,7 @@ money32 string_to_money(const char* string_to_monetise) else if (*src_ptr == '-') { if (hasMinus) - return MONEY32_UNDEFINED; + return MONEY64_UNDEFINED; hasMinus = true; } else @@ -475,16 +475,16 @@ money32 string_to_money(const char* string_to_monetise) *dst_ptr = '\0'; if (numNumbers == 0) - return MONEY32_UNDEFINED; + return MONEY64_UNDEFINED; - int32_t sign = 1; + int64_t sign = 1; if (hasMinus) { // If there is a minus sign, it has to be at position 0 in order to be valid. if (processedString[0] == '-') sign = -1; else - return MONEY32_UNDEFINED; + return MONEY64_UNDEFINED; } // Due to the nature of strstr and strtok, decimals at the very beginning will be ignored, causing @@ -499,10 +499,7 @@ money32 string_to_money(const char* string_to_monetise) auto number = std::stod(processedString, nullptr); number /= (currencyDesc->rate / 10.0); - // Check if MONEY resulted in overflow - uint64_t result = std::min(ToMoney32FromGBP(number), (std::numeric_limits::max)()); - result *= sign; - return static_cast(result); + return ToMoney64FromGBP(number) * sign; } /** @@ -513,39 +510,40 @@ money32 string_to_money(const char* string_to_monetise) * @param forceDecimals Show decimals, even if the amount does not have them. Will be ignored if the current exchange * rate is too big to have decimals. */ -void money_to_string(money32 amount, char* buffer_to_put_value_to, size_t buffer_len, bool forceDecimals) +void money_to_string(money64 amount, char* buffer_to_put_value_to, size_t buffer_len, bool forceDecimals) { - if (amount == MONEY32_UNDEFINED) + if (amount == MONEY64_UNDEFINED) { snprintf(buffer_to_put_value_to, buffer_len, "0"); return; } - const currency_descriptor* currencyDesc = &CurrencyDescriptors[EnumValue(gConfigGeneral.CurrencyFormat)]; + const currency_descriptor& currencyDesc = CurrencyDescriptors[EnumValue(gConfigGeneral.CurrencyFormat)]; - int sign = amount >= 0 ? 1 : -1; - int a = abs(amount) * currencyDesc->rate; + const char* sign = amount >= 0 ? "" : "-"; + const uint64_t a = std::abs(amount) * currencyDesc.rate; + const unsigned long long whole = a / 100; + const unsigned long long decimal = a % 100; - bool amountIsInteger = (a / 100 > 0) && (a % 100 == 0); + bool amountIsInteger = (whole > 0) && decimal == 0; // If whole and decimal exist - if ((a / 100 > 0 && a % 100 > 0) || (amountIsInteger && forceDecimals && currencyDesc->rate < 100)) + if ((whole > 0 && decimal > 0) || (amountIsInteger && forceDecimals && currencyDesc.rate < 100)) { - const char* decimal_char = language_get_string(STR_LOCALE_DECIMAL_POINT); - auto decimalPart = a % 100; - auto precedingZero = (decimalPart < 10) ? "0" : ""; - snprintf(buffer_to_put_value_to, buffer_len, "%d%s%s%d", (a / 100) * sign, decimal_char, precedingZero, decimalPart); + const char* decimalChar = language_get_string(STR_LOCALE_DECIMAL_POINT); + auto precedingZero = (decimal < 10) ? "0" : ""; + snprintf(buffer_to_put_value_to, buffer_len, "%s%llu%s%s%llu", sign, whole, decimalChar, precedingZero, decimal); } // If whole exists, but not decimal else if (amountIsInteger) { - snprintf(buffer_to_put_value_to, buffer_len, "%d", (a / 100) * sign); + snprintf(buffer_to_put_value_to, buffer_len, "%s%llu", sign, whole); } // If decimal exists, but not whole - else if (a / 100 == 0 && a % 100 > 0) + else if (whole == 0 && decimal > 0) { - const char* decimal_char = language_get_string(STR_LOCALE_DECIMAL_POINT); - snprintf(buffer_to_put_value_to, buffer_len, "%s0%s%d", sign < 0 ? "-" : "", decimal_char, a % 100); + const char* decimalChar = language_get_string(STR_LOCALE_DECIMAL_POINT); + snprintf(buffer_to_put_value_to, buffer_len, "%s0%s%llu", sign, decimalChar, decimal); } else { diff --git a/src/openrct2/localisation/Localisation.h b/src/openrct2/localisation/Localisation.h index 18adbb46ed..be21938b97 100644 --- a/src/openrct2/localisation/Localisation.h +++ b/src/openrct2/localisation/Localisation.h @@ -38,8 +38,8 @@ size_t get_string_size(const utf8* text); // The maximum number of characters allowed for string/money conversions (anything above will risk integer overflow issues) #define MONEY_STRING_MAXLENGTH 14 -money32 string_to_money(const char* string_to_monetise); -void money_to_string(money32 amount, char* buffer_to_put_value_to, size_t buffer_len, bool forceDecimals); +money64 string_to_money(const char* string_to_monetise); +void money_to_string(money64 amount, char* buffer_to_put_value_to, size_t buffer_len, bool forceDecimals); bool is_user_string_id(StringId stringId); diff --git a/src/openrct2/world/Park.cpp b/src/openrct2/world/Park.cpp index 1254b2ce87..5f61ba6bcc 100644 --- a/src/openrct2/world/Park.cpp +++ b/src/openrct2/world/Park.cpp @@ -500,7 +500,7 @@ money64 Park::CalculateRideValue(const Ride& ride) const if (ride.value != RIDE_VALUE_UNDEFINED) { const auto& rtd = ride.GetRideTypeDescriptor(); - result = ToMoney32FromGBP(static_cast(ride.value)) + result = ToMoney64FromGBP(ride.value) * (static_cast(ride_customers_in_last_5_minutes(ride)) + rtd.BonusValue * 4LL); } return result;