diff --git a/src/command.cpp b/src/command.cpp index 51ec446e77..d7c0efb815 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -161,21 +161,6 @@ bool IsCommandAllowedWhilePaused(Commands cmd) return _game_mode == GM_EDITOR || command_type_lookup[_command_proc_table[cmd].type] <= _settings_game.construction.command_pause_level; } -/*! - * This functions returns the money which can be used to execute a command. - * This is either the money of the current company or INT64_MAX if there - * is no such a company "at the moment" like the server itself. - * - * @return The available money of a company or INT64_MAX - */ -Money GetAvailableMoneyForCommand() -{ - CompanyID company = _current_company; - if (!Company::IsValidID(company)) return INT64_MAX; - return Company::Get(company)->money; -} - - /** * Prepare for calling a command proc. * @param top_level Top level of command execution, i.e. command from a command. diff --git a/src/command_func.h b/src/command_func.h index 47ce1ffe24..f0da57c65e 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -42,7 +42,6 @@ void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *cal bool IsValidCommand(Commands cmd); CommandFlags GetCommandFlags(Commands cmd); const char *GetCommandName(Commands cmd); -Money GetAvailableMoneyForCommand(); bool IsCommandAllowedWhilePaused(Commands cmd); template diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 6605ef9878..b17c13fc93 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -193,20 +193,48 @@ void InvalidateCompanyWindows(const Company *company) SetWindowDirty(WC_FINANCES, cid); } +/** + * Get the amount of money that a company has available, or INT64_MAX + * if there is no such valid company. + * + * @param company Company to check + * @return The available money of the company or INT64_MAX + */ +Money GetAvailableMoney(CompanyID company) +{ + if (_settings_game.difficulty.infinite_money) return INT64_MAX; + if (!Company::IsValidID(company)) return INT64_MAX; + return Company::Get(company)->money; +} + +/** + * This functions returns the money which can be used to execute a command. + * This is either the money of the current company, or INT64_MAX if infinite money + * is enabled or there is no such a company "at the moment" like the server itself. + * + * @return The available money of the current company or INT64_MAX + */ +Money GetAvailableMoneyForCommand() +{ + return GetAvailableMoney(_current_company); +} + /** * Verify whether the company can pay the bill. * @param[in,out] cost Money to pay, is changed to an error if the company does not have enough money. - * @return Function returns \c true if the company has enough money, else it returns \c false. + * @return Function returns \c true if the company has enough money or infinite money is enabled, + * else it returns \c false. */ bool CheckCompanyHasMoney(CommandCost &cost) { - if (cost.GetCost() > 0) { - const Company *c = Company::GetIfValid(_current_company); - if (c != nullptr && cost.GetCost() > c->money) { - SetDParam(0, cost.GetCost()); - cost.MakeError(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); - return false; - } + if (cost.GetCost() <= 0) return true; + if (_settings_game.difficulty.infinite_money) return true; + + const Company *c = Company::GetIfValid(_current_company); + if (c != nullptr && cost.GetCost() > c->money) { + SetDParam(0, cost.GetCost()); + cost.MakeError(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); + return false; } return true; } diff --git a/src/company_func.h b/src/company_func.h index 47adc24870..5d2d0df1dd 100644 --- a/src/company_func.h +++ b/src/company_func.h @@ -26,6 +26,8 @@ void CompanyAdminBankrupt(CompanyID company_id); void UpdateLandscapingLimits(); void UpdateCompanyLiveries(Company *c); +Money GetAvailableMoney(CompanyID company); +Money GetAvailableMoneyForCommand(); bool CheckCompanyHasMoney(CommandCost &cost); void SubtractMoneyFromCompany(const CommandCost &cost); void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cost); diff --git a/src/economy.cpp b/src/economy.cpp index 840822b10c..decc19e8e1 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -568,6 +568,9 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner) */ static void CompanyCheckBankrupt(Company *c) { + /* If "Infinite money" setting is on, companies should not go bankrupt. */ + if (_settings_game.difficulty.infinite_money) return; + /* If the company has money again, it does not go bankrupt */ if (c->money - c->current_loan >= -_economy.max_loan) { int previous_months_of_bankruptcy = CeilDiv(c->months_of_bankruptcy, 3); diff --git a/src/lang/english.txt b/src/lang/english.txt index 66c1e89598..3f09b0863c 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -835,6 +835,7 @@ STR_STATUSBAR_AUTOSAVE :{RED}AUTOSAVE STR_STATUSBAR_SAVING_GAME :{RED}* * SAVING GAME * * STR_STATUSBAR_SPECTATOR :{WHITE}(spectator) +STR_STATUSBAR_INFINITE_MONEY :{WHITE}(infinite money) # News message history STR_MESSAGE_HISTORY :{WHITE}Message History @@ -1287,6 +1288,9 @@ STR_CONFIG_SETTING_HORIZONTAL_POS_RIGHT :Right STR_CONFIG_SETTING_SECONDS_VALUE :{COMMA}{NBSP}second{P 0 "" s} +STR_CONFIG_SETTING_INFINITE_MONEY :Infinite money: {STRING2} +STR_CONFIG_SETTING_INFINITE_MONEY_HELPTEXT :Allow unlimited spending and disable bankruptcy of companies + STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN :Maximum initial loan: {STRING2} STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_HELPTEXT :Maximum amount a company can loan (without taking inflation into account) STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_VALUE :{CURRENCY_LONG} diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index 066e4a2f55..81764fccf2 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -10,6 +10,7 @@ #include "stdafx.h" #include "landscape.h" #include "command_func.h" +#include "company_func.h" #include "viewport_func.h" #include "company_base.h" #include "town.h" diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index ffaf3e5cad..1d814a65ee 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -12,6 +12,7 @@ #include "road_internal.h" #include "viewport_func.h" #include "command_func.h" +#include "company_func.h" #include "pathfinder/yapf/yapf_cache.h" #include "depot_base.h" #include "newgrf.h" diff --git a/src/script/api/script_company.cpp b/src/script/api/script_company.cpp index b1d5a10d5f..482583de3c 100644 --- a/src/script/api/script_company.cpp +++ b/src/script/api/script_company.cpp @@ -179,7 +179,7 @@ company = ResolveCompanyID(company); if (company == COMPANY_INVALID) return -1; - return ::Company::Get(company)->money; + return GetAvailableMoney((::CompanyID)company); } /* static */ Money ScriptCompany::GetLoanAmount() diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index f335d10a18..9156f05aef 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2084,6 +2084,7 @@ static SettingsContainer &GetSettingsTree() SettingsPage *accounting = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCOUNTING)); { + accounting->Add(new SettingEntry("difficulty.infinite_money")); accounting->Add(new SettingEntry("economy.inflation")); accounting->Add(new SettingEntry("difficulty.initial_interest")); accounting->Add(new SettingEntry("difficulty.max_loan")); diff --git a/src/settings_type.h b/src/settings_type.h index e9c6a60038..8a601b28b7 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -114,6 +114,7 @@ struct DifficultySettings { bool line_reverse_mode; ///< reversing at stations or not bool disasters; ///< are disasters enabled byte town_council_tolerance; ///< minimum required town ratings to be allowed to demolish stuff + bool infinite_money; ///< whether spending money despite negative balance is allowed }; /** Settings relating to viewport/smallmap scrolling. */ diff --git a/src/statusbar_gui.cpp b/src/statusbar_gui.cpp index 856b4cf077..19db459763 100644 --- a/src/statusbar_gui.cpp +++ b/src/statusbar_gui.cpp @@ -123,6 +123,8 @@ struct StatusBarWindow : Window { case WID_S_RIGHT: { if (_local_company == COMPANY_SPECTATOR) { DrawString(tr, STR_STATUSBAR_SPECTATOR, TC_FROMSTRING, SA_HOR_CENTER); + } else if (_settings_game.difficulty.infinite_money) { + DrawString(tr, STR_STATUSBAR_INFINITE_MONEY, TC_FROMSTRING, SA_HOR_CENTER); } else { /* Draw company money, if any */ const Company *c = Company::GetIfValid(_local_company); diff --git a/src/table/settings/difficulty_settings.ini b/src/table/settings/difficulty_settings.ini index 22128e9ac8..0cf90608c3 100644 --- a/src/table/settings/difficulty_settings.ini +++ b/src/table/settings/difficulty_settings.ini @@ -298,3 +298,11 @@ min = 0 max = 3 cat = SC_BASIC +[SDT_BOOL] +var = difficulty.infinite_money +def = false +str = STR_CONFIG_SETTING_INFINITE_MONEY +strhelp = STR_CONFIG_SETTING_INFINITE_MONEY_HELPTEXT +cat = SC_BASIC +post_cb = [](auto) { SetWindowDirty(WC_STATUS_BAR, 0); } + diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index b68e61c476..eaf58cfdb3 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -15,6 +15,7 @@ #include "viewport_func.h" #include "viewport_kdtree.h" #include "command_func.h" +#include "company_func.h" #include "industry.h" #include "station_base.h" #include "waypoint_base.h" diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 5caa021139..5acb56f853 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -220,7 +220,7 @@ bool Vehicle::NeedsServicing() const * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */ bool pending_replace = false; Money needed_money = c->settings.engine_renew_money; - if (needed_money > c->money) return false; + if (needed_money > GetAvailableMoney(c->index)) return false; for (const Vehicle *v = this; v != nullptr; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : nullptr) { bool replace_when_old = false; @@ -258,7 +258,7 @@ bool Vehicle::NeedsServicing() const * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */ pending_replace = true; needed_money += 2 * Engine::Get(new_engine)->GetCost(); - if (needed_money > c->money) return false; + if (needed_money > GetAvailableMoney(c->index)) return false; } return pending_replace;