From 897b59c15868a93d4455b9a2312514ef94f0e237 Mon Sep 17 00:00:00 2001 From: dP Date: Tue, 30 Jan 2024 23:45:19 +0530 Subject: [PATCH] Add: [GS] Allow to set max loan for each company separately (#11224) --- src/command_type.h | 1 + src/company_base.h | 10 ++++-- src/company_cmd.cpp | 10 ++++++ src/company_gui.cpp | 8 +++-- src/economy.cpp | 2 +- src/economy_type.h | 2 ++ src/misc_cmd.cpp | 36 +++++++++++++++++++--- src/misc_cmd.h | 12 +++++--- src/saveload/afterload.cpp | 6 ++++ src/saveload/company_sl.cpp | 1 + src/saveload/saveload.h | 2 ++ src/script/api/game_changelog.hpp | 2 ++ src/script/api/script_company.cpp | 27 +++++++++++++++- src/script/api/script_company.hpp | 28 ++++++++++++++++- src/table/settings/difficulty_settings.ini | 2 +- 15 files changed, 129 insertions(+), 20 deletions(-) diff --git a/src/command_type.h b/src/command_type.h index 1e4fd78df1..b3ca60d901 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -253,6 +253,7 @@ enum Commands : uint16_t { CMD_INCREASE_LOAN, ///< increase the loan from the bank CMD_DECREASE_LOAN, ///< decrease the loan from the bank + CMD_SET_COMPANY_MAX_LOAN, ///< sets the max loan for the company CMD_WANT_ENGINE_PREVIEW, ///< confirm the preview of an engine CMD_ENGINE_CTRL, ///< control availability of the engine for companies diff --git a/src/company_base.h b/src/company_base.h index d78b5b402f..1264aaa2a8 100644 --- a/src/company_base.h +++ b/src/company_base.h @@ -18,6 +18,8 @@ #include "settings_type.h" #include "group.h" +static const Money COMPANY_MAX_LOAN_DEFAULT = INT64_MIN; + /** Statistics about the economy. */ struct CompanyEconomyEntry { Money income; ///< The amount of income. @@ -50,7 +52,6 @@ struct CompanyInfrastructure { typedef Pool CompanyPool; extern CompanyPool _company_pool; - /** Statically loadable part of Company pool item */ struct CompanyProperties { uint32_t name_2; ///< Parameter of #name_1. @@ -66,6 +67,7 @@ struct CompanyProperties { Money money; ///< Money owned by the company. byte money_fraction; ///< Fraction of money of the company, too small to represent in #money. Money current_loan; ///< Amount of money borrowed from the bank. + Money max_loan; ///< Max allowed amount of the loan or COMPANY_MAX_LOAN_DEFAULT. Colours colour; ///< Company colour. @@ -105,8 +107,8 @@ struct CompanyProperties { // TODO: Change some of these member variables to use relevant INVALID_xxx constants CompanyProperties() : name_2(0), name_1(0), president_name_1(0), president_name_2(0), - face(0), money(0), money_fraction(0), current_loan(0), colour(COLOUR_BEGIN), block_preview(0), - location_of_HQ(0), last_build_coordinate(0), inaugurated_year(0), + face(0), money(0), money_fraction(0), current_loan(0), max_loan(COMPANY_MAX_LOAN_DEFAULT), + colour(COLOUR_BEGIN), block_preview(0), location_of_HQ(0), last_build_coordinate(0), inaugurated_year(0), months_of_bankruptcy(0), bankrupt_asked(0), bankrupt_timeout(0), bankrupt_value(0), terraform_limit(0), clear_limit(0), tree_limit(0), build_object_limit(0), is_ai(false), engine_renew_list(nullptr) {} }; @@ -126,6 +128,8 @@ struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> { CompanyInfrastructure infrastructure; ///< NOSAVE: Counts of company owned infrastructure. + Money GetMaxLoan() const; + /** * Is this company a valid company, controlled by the computer (a NoAI program)? * @param index Index in the pool. diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index b17c13fc93..8677367ff7 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -95,6 +95,16 @@ void Company::PostDestructor(size_t index) InvalidateWindowData(WC_ERRMSG, 0); } +/** + * Calculate the max allowed loan for this company. + * @return the max loan amount. + */ +Money Company::GetMaxLoan() const +{ + if (this->max_loan == COMPANY_MAX_LOAN_DEFAULT) return _economy.max_loan; + return this->max_loan; +} + /** * Sets the local company and updates the settings that are set on a * per-company basis to reflect the core's state in the GUI. diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 6d354d86bd..3785d11693 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -374,9 +374,11 @@ struct CompanyFinancesWindow : Window { SetDParam(0, _settings_game.difficulty.initial_interest); break; - case WID_CF_MAXLOAN_VALUE: - SetDParam(0, _economy.max_loan); + case WID_CF_MAXLOAN_VALUE: { + const Company *c = Company::Get((CompanyID)this->window_number); + SetDParam(0, c->GetMaxLoan()); break; + } case WID_CF_INCREASE_LOAN: case WID_CF_REPAY_LOAN: @@ -474,7 +476,7 @@ struct CompanyFinancesWindow : Window { } const Company *c = Company::Get(company); - this->SetWidgetDisabledState(WID_CF_INCREASE_LOAN, c->current_loan == _economy.max_loan); // Borrow button only shows when there is any more money to loan. + this->SetWidgetDisabledState(WID_CF_INCREASE_LOAN, c->current_loan >= c->GetMaxLoan()); // Borrow button only shows when there is any more money to loan. this->SetWidgetDisabledState(WID_CF_REPAY_LOAN, company != _local_company || c->current_loan == 0); // Repay button only shows when there is any more money to repay. } diff --git a/src/economy.cpp b/src/economy.cpp index decc19e8e1..c7bebd0936 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -572,7 +572,7 @@ static void CompanyCheckBankrupt(Company *c) 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) { + if (c->money - c->current_loan >= -c->GetMaxLoan()) { int previous_months_of_bankruptcy = CeilDiv(c->months_of_bankruptcy, 3); c->months_of_bankruptcy = 0; c->bankrupt_asked = 0; diff --git a/src/economy_type.h b/src/economy_type.h index 763634ac3b..c565e3257b 100644 --- a/src/economy_type.h +++ b/src/economy_type.h @@ -200,6 +200,8 @@ struct PriceBaseSpec { static const int LOAN_INTERVAL = 10000; /** The size of loan for a new company, in British Pounds! */ static const int64_t INITIAL_LOAN = 100000; +/** The max amount possible to configure for a max loan of a company. */ +static const int64_t MAX_LOAN_LIMIT = 2000000000; /** * Maximum inflation (including fractional part) without causing overflows in int64_t price computations. diff --git a/src/misc_cmd.cpp b/src/misc_cmd.cpp index aa2f4d6029..2b95ca1eb5 100644 --- a/src/misc_cmd.cpp +++ b/src/misc_cmd.cpp @@ -39,9 +39,9 @@ CommandCost CmdIncreaseLoan(DoCommandFlag flags, LoanCommand cmd, Money amount) { Company *c = Company::Get(_current_company); - - if (c->current_loan >= _economy.max_loan) { - SetDParam(0, _economy.max_loan); + Money max_loan = c->GetMaxLoan(); + if (c->current_loan >= max_loan) { + SetDParam(0, max_loan); return_cmd_error(STR_ERROR_MAXIMUM_PERMITTED_LOAN); } @@ -52,11 +52,11 @@ CommandCost CmdIncreaseLoan(DoCommandFlag flags, LoanCommand cmd, Money amount) loan = LOAN_INTERVAL; break; case LoanCommand::Max: // Take a loan as big as possible - loan = _economy.max_loan - c->current_loan; + loan = max_loan - c->current_loan; break; case LoanCommand::Amount: // Take the given amount of loan loan = amount; - if (loan < LOAN_INTERVAL || c->current_loan + loan > _economy.max_loan || loan % LOAN_INTERVAL != 0) return CMD_ERROR; + if (loan < LOAN_INTERVAL || c->current_loan + loan > max_loan || loan % LOAN_INTERVAL != 0) return CMD_ERROR; break; } @@ -118,6 +118,32 @@ CommandCost CmdDecreaseLoan(DoCommandFlag flags, LoanCommand cmd, Money amount) return CommandCost(); } +/** + * Sets the max loan amount of your company. Does not respect the global loan setting. + * @param company the company ID. + * @param amount the new max loan amount, will be rounded down to the multitude of LOAN_INTERVAL. If set to COMPANY_MAX_LOAN_DEFAULT reset the max loan to default(global) value. + * @return zero cost or an error + */ +CommandCost CmdSetCompanyMaxLoan(DoCommandFlag flags, CompanyID company, Money amount) +{ + if (_current_company != OWNER_DEITY) return CMD_ERROR; + if (amount != COMPANY_MAX_LOAN_DEFAULT) { + if (amount < 0 || amount > (Money)MAX_LOAN_LIMIT) return CMD_ERROR; + } + + Company *c = Company::GetIfValid(company); + if (c == nullptr) return CMD_ERROR; + + if (flags & DC_EXEC) { + /* Round the amount down to a multiple of LOAN_INTERVAL. */ + if (amount != COMPANY_MAX_LOAN_DEFAULT) amount -= (int64_t)amount % LOAN_INTERVAL; + + c->max_loan = amount; + InvalidateCompanyWindows(c); + } + return CommandCost(); +} + /** * In case of an unsafe unpause, we want the * user to confirm that it might crash. diff --git a/src/misc_cmd.h b/src/misc_cmd.h index 77250b551c..47cdf760ae 100644 --- a/src/misc_cmd.h +++ b/src/misc_cmd.h @@ -25,12 +25,14 @@ CommandCost CmdMoneyCheat(DoCommandFlag flags, Money amount); CommandCost CmdChangeBankBalance(DoCommandFlag flags, TileIndex tile, Money delta, CompanyID company, ExpensesType expenses_type); CommandCost CmdIncreaseLoan(DoCommandFlag flags, LoanCommand cmd, Money amount); CommandCost CmdDecreaseLoan(DoCommandFlag flags, LoanCommand cmd, Money amount); +CommandCost CmdSetCompanyMaxLoan(DoCommandFlag flags, CompanyID company, Money amount); CommandCost CmdPause(DoCommandFlag flags, PauseMode mode, bool pause); -DEF_CMD_TRAIT(CMD_MONEY_CHEAT, CmdMoneyCheat, CMD_OFFLINE, CMDT_CHEAT) -DEF_CMD_TRAIT(CMD_CHANGE_BANK_BALANCE, CmdChangeBankBalance, CMD_DEITY, CMDT_MONEY_MANAGEMENT) -DEF_CMD_TRAIT(CMD_INCREASE_LOAN, CmdIncreaseLoan, 0, CMDT_MONEY_MANAGEMENT) -DEF_CMD_TRAIT(CMD_DECREASE_LOAN, CmdDecreaseLoan, 0, CMDT_MONEY_MANAGEMENT) -DEF_CMD_TRAIT(CMD_PAUSE, CmdPause, CMD_SERVER | CMD_NO_EST, CMDT_SERVER_SETTING) +DEF_CMD_TRAIT(CMD_MONEY_CHEAT, CmdMoneyCheat, CMD_OFFLINE, CMDT_CHEAT) +DEF_CMD_TRAIT(CMD_CHANGE_BANK_BALANCE, CmdChangeBankBalance, CMD_DEITY, CMDT_MONEY_MANAGEMENT) +DEF_CMD_TRAIT(CMD_INCREASE_LOAN, CmdIncreaseLoan, 0, CMDT_MONEY_MANAGEMENT) +DEF_CMD_TRAIT(CMD_DECREASE_LOAN, CmdDecreaseLoan, 0, CMDT_MONEY_MANAGEMENT) +DEF_CMD_TRAIT(CMD_SET_COMPANY_MAX_LOAN, CmdSetCompanyMaxLoan, CMD_DEITY, CMDT_MONEY_MANAGEMENT) +DEF_CMD_TRAIT(CMD_PAUSE, CmdPause, CMD_SERVER | CMD_NO_EST, CMDT_SERVER_SETTING) #endif /* MISC_CMD_H */ diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 25f3fe6451..bc9072a3a5 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3280,6 +3280,12 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_MAX_LOAN_FOR_COMPANY)) { + for (Company *c : Company::Iterate()) { + c->max_loan = COMPANY_MAX_LOAN_DEFAULT; + } + } + for (Company *c : Company::Iterate()) { UpdateCompanyLiveries(c); } diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 6ed8c5a8c4..99e8b43a51 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -458,6 +458,7 @@ static const SaveLoad _company_desc[] = { SLE_CONDVAR(CompanyProperties, current_loan, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_65), SLE_CONDVAR(CompanyProperties, current_loan, SLE_INT64, SLV_65, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, max_loan, SLE_INT64, SLV_MAX_LOAN_FOR_COMPANY, SL_MAX_VERSION), SLE_VAR(CompanyProperties, colour, SLE_UINT8), SLE_VAR(CompanyProperties, money_fraction, SLE_UINT8), diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 82c2d4b1f2..7ba2fdf967 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -373,6 +373,8 @@ enum SaveLoadVersion : uint16_t { SLV_CALENDAR_SUB_DATE_FRACT, ///< 328 PR#11428 Add sub_date_fract to measure calendar days. SLV_SHIP_ACCELERATION, ///< 329 PR#10734 Start using Vehicle's acceleration field for ships too. + SLV_MAX_LOAN_FOR_COMPANY, ///< 330 PR#11224 Separate max loan for each company. + SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index 71ff4b6d5a..d3f4ac8d91 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -47,6 +47,8 @@ * \li GSCompany::SetAutoRenewStatus * \li GSCompany::SetAutoRenewMonths * \li GSCompany::SetAutoRenewMoney + * \li GSCompany::SetMaxLoanAmountForCompany + * \li GSCompany::ResetMaxLoanAmountForCompany * \li GSGameSettings::IsDisabledVehicleType * \li GSGroup::GroupID * \li GSGroup::IsValidGroup diff --git a/src/script/api/script_company.cpp b/src/script/api/script_company.cpp index 482583de3c..717aee5f50 100644 --- a/src/script/api/script_company.cpp +++ b/src/script/api/script_company.cpp @@ -192,7 +192,32 @@ /* static */ Money ScriptCompany::GetMaxLoanAmount() { - return _economy.max_loan; + if (ScriptCompanyMode::IsDeity()) return _economy.max_loan; + + ScriptCompany::CompanyID company = ResolveCompanyID(COMPANY_SELF); + if (company == COMPANY_INVALID) return -1; + + return ::Company::Get(company)->GetMaxLoan(); +} + +/* static */ bool ScriptCompany::SetMaxLoanAmountForCompany(CompanyID company, Money amount) +{ + EnforceDeityMode(false); + EnforcePrecondition(false, amount >= 0 && amount <= (Money)MAX_LOAN_LIMIT); + + company = ResolveCompanyID(company); + EnforcePrecondition(false, company != COMPANY_INVALID); + return ScriptObject::Command::Do((::CompanyID)company, amount); +} + +/* static */ bool ScriptCompany::ResetMaxLoanAmountForCompany(CompanyID company) +{ + EnforceDeityMode(false); + + company = ResolveCompanyID(company); + EnforcePrecondition(false, company != COMPANY_INVALID); + + return ScriptObject::Command::Do((::CompanyID)company, COMPANY_MAX_LOAN_DEFAULT); } /* static */ Money ScriptCompany::GetLoanInterval() diff --git a/src/script/api/script_company.hpp b/src/script/api/script_company.hpp index 6e4ef1e2ae..762b8ffcdf 100644 --- a/src/script/api/script_company.hpp +++ b/src/script/api/script_company.hpp @@ -217,12 +217,38 @@ public: static Money GetLoanAmount(); /** - * Gets the maximum amount your company can loan. + * Gets the maximum amount your company can loan. In deity mode returns the global max loan. * @return The maximum amount your company can loan. * @post GetLoanInterval() is always a multiplier of the return value. */ static Money GetMaxLoanAmount(); + /** + * Sets the max amount of money company can loan. + * @param company The company ID. + * @param amount Max loan amount. Will be rounded down to a multiple of GetLoanInterval(). + * @return True, if the max loan was changed. + * @pre ScriptCompanyMode::IsDeity(). + * @pre amount >= 0. + * @pre ResolveCompanyID(company) != COMPANY_INVALID. + * @note You need to create your own news message to inform about max loan change. + * @note Max loan value set with this method is not affected by inflation. + * @api -ai + */ + static bool SetMaxLoanAmountForCompany(CompanyID company, Money amount); + + /** + * Makes the max amount of money company can loan follow the global max loan setting. + * @param company The company ID. + * @return True, if the max loan was reset. + * @pre ScriptCompanyMode::IsDeity(). + * @pre amount >= 0 && amount <= MAX_LOAN_LIMIT. + * @pre ResolveCompanyID(company) != COMPANY_INVALID. + * @note You need to create your own news message to inform about max loan change. + * @api -ai + */ + static bool ResetMaxLoanAmountForCompany(CompanyID company); + /** * Gets the interval/loan step. * @return The loan step. diff --git a/src/table/settings/difficulty_settings.ini b/src/table/settings/difficulty_settings.ini index 0cf90608c3..9503f99399 100644 --- a/src/table/settings/difficulty_settings.ini +++ b/src/table/settings/difficulty_settings.ini @@ -111,7 +111,7 @@ from = SLV_97 flags = SF_NEWGAME_ONLY | SF_SCENEDIT_TOO | SF_GUI_CURRENCY | SF_GUI_0_IS_SPECIAL def = 300000 min = LOAN_INTERVAL -max = 2000000000 +max = MAX_LOAN_LIMIT pre_cb = [](auto &new_value) { new_value = (new_value + LOAN_INTERVAL / 2) / LOAN_INTERVAL * LOAN_INTERVAL; return true; } interval = LOAN_INTERVAL str = STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN