diff --git a/cmake/InstallAndPackage.cmake b/cmake/InstallAndPackage.cmake index 6fbae25732..6608d21663 100644 --- a/cmake/InstallAndPackage.cmake +++ b/cmake/InstallAndPackage.cmake @@ -23,16 +23,28 @@ install(TARGETS openttd COMPONENT Runtime ) -install(DIRECTORY - ${CMAKE_BINARY_DIR}/lang - ${CMAKE_BINARY_DIR}/baseset - ${CMAKE_BINARY_DIR}/ai - ${CMAKE_BINARY_DIR}/game - ${CMAKE_SOURCE_DIR}/bin/scripts - DESTINATION ${DATA_DESTINATION_DIR} - COMPONENT language_files - REGEX "ai/[^\.]+$" EXCLUDE # Ignore subdirs in ai dir -) +if (NOT EMSCRIPTEN) + # Emscripten embeds these files in openttd.data. + # See CMakeLists.txt in the root. + install(DIRECTORY + ${CMAKE_BINARY_DIR}/lang + ${CMAKE_BINARY_DIR}/baseset + ${CMAKE_BINARY_DIR}/ai + ${CMAKE_BINARY_DIR}/game + ${CMAKE_SOURCE_DIR}/bin/scripts + DESTINATION ${DATA_DESTINATION_DIR} + COMPONENT language_files + REGEX "ai/[^\.]+$" EXCLUDE # Ignore subdirs in ai dir + ) +else() + install(FILES + ${CMAKE_BINARY_DIR}/openttd.js + ${CMAKE_BINARY_DIR}/openttd.wasm + ${CMAKE_BINARY_DIR}/openttd.data + DESTINATION ${BINARY_DESTINATION_DIR} + COMPONENT Runtime + ) +endif() install(FILES ${CMAKE_SOURCE_DIR}/COPYING.md @@ -80,7 +92,7 @@ if(OPTION_INSTALL_FHS) COMPONENT manual) endif() -if(UNIX AND NOT APPLE) +if(UNIX AND NOT APPLE AND NOT EMSCRIPTEN) install(DIRECTORY ${CMAKE_BINARY_DIR}/media/icons ${CMAKE_BINARY_DIR}/media/pixmaps diff --git a/cmake/scripts/Regression.cmake b/cmake/scripts/Regression.cmake index c719b90406..af5471799f 100644 --- a/cmake/scripts/Regression.cmake +++ b/cmake/scripts/Regression.cmake @@ -34,7 +34,6 @@ execute_process(COMMAND ${OPENTTD_EXECUTABLE} -mnull -vnull:ticks=30000 -d script=2 - -d misc=9 -Q OUTPUT_VARIABLE REGRESSION_OUTPUT ERROR_VARIABLE REGRESSION_RESULT @@ -58,16 +57,24 @@ string(REPLACE "0x0x0" "0x00000000" REGRESSION_RESULT "${REGRESSION_RESULT}") string(REPLACE "\\" "/" REGRESSION_RESULT "${REGRESSION_RESULT}") # Remove timestamps if any -string(REGEX REPLACE "\[[0-9-]+ [0-9:]+\] " "" REGRESSION_RESULT "${REGRESSION_RESULT}") +string(REGEX REPLACE "\\\[[0-9-]+ [0-9:]+\\\] " "" REGRESSION_RESULT "${REGRESSION_RESULT}") + +# Remove log level +string(REGEX REPLACE "\\\[script:[0-9]\\\]" "" REGRESSION_RESULT "${REGRESSION_RESULT}") # Convert the output to a format that is expected (and more readable) by result.txt -string(REPLACE "\ndbg: [script]" "\n" REGRESSION_RESULT "${REGRESSION_RESULT}") +string(REPLACE "\ndbg: " "\n" REGRESSION_RESULT "${REGRESSION_RESULT}") string(REPLACE "\n " "\nERROR: " REGRESSION_RESULT "${REGRESSION_RESULT}") string(REPLACE "\nERROR: [1] " "\n" REGRESSION_RESULT "${REGRESSION_RESULT}") string(REPLACE "\n[P] " "\n" REGRESSION_RESULT "${REGRESSION_RESULT}") string(REPLACE "\n[S] " "\n" REGRESSION_RESULT "${REGRESSION_RESULT}") string(REGEX REPLACE "dbg: ([^\n]*)\n?" "" REGRESSION_RESULT "${REGRESSION_RESULT}") +# Remove duplicate script info +string(REGEX REPLACE "ERROR: Registering([^\n]*)\n?" "" REGRESSION_RESULT "${REGRESSION_RESULT}") +string(REGEX REPLACE "ERROR: [12]([^\n]*)\n?" "" REGRESSION_RESULT "${REGRESSION_RESULT}") +string(REGEX REPLACE "ERROR: The first([^\n]*)\n?" "" REGRESSION_RESULT "${REGRESSION_RESULT}") + # Read the expected result file(READ ai/${REGRESSION_TEST}/result.txt REGRESSION_EXPECTED) @@ -91,7 +98,7 @@ foreach(RESULT IN LISTS REGRESSION_RESULT) if(NOT RESULT STREQUAL EXPECTED) message("${ARGC}: - ${EXPECTED}") - message("${ARGC}: + ${RESULT}'") + message("${ARGC}: + ${RESULT}") set(ERROR YES) endif() endforeach() diff --git a/regression/regression/result.txt b/regression/regression/result.txt index 780460af17..07feb370b2 100644 --- a/regression/regression/result.txt +++ b/regression/regression/result.txt @@ -1,4 +1,3 @@ - --TestInit-- Ops: 9988 TickTest: 1 diff --git a/regression/stationlist/result.txt b/regression/stationlist/result.txt index 20e594766f..c04a306358 100644 --- a/regression/stationlist/result.txt +++ b/regression/stationlist/result.txt @@ -1,4 +1,3 @@ - --StationList-- Count(): 5 Location ListDump: diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp index cfc3ef6513..86d68a7122 100644 --- a/src/ai/ai_core.cpp +++ b/src/ai/ai_core.cpp @@ -50,6 +50,7 @@ /* Load default data and store the name in the settings */ config->Change(info->GetName(), -1, false, true); } + if (rerandomise_ai) config->AddRandomDeviation(); config->AnchorUnchangeableSettings(); Backup cur_company(_current_company, company, FILE_LINE); diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 5a6a52cb53..d4d5a7e192 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -180,8 +180,16 @@ struct AIConfigWindow : public Window { } else { text = STR_AI_CONFIG_RANDOM_AI; } - DrawString(tr, text, - (this->selected_slot == i) ? TC_WHITE : (IsEditable((CompanyID)i) ? TC_ORANGE : TC_SILVER)); + + TextColour tc = TC_SILVER; + if (this->selected_slot == i) { + tc = TC_WHITE; + } else if (IsEditable((CompanyID)i)) { + tc = TC_ORANGE; + } else if (Company::IsValidAiID(i)) { + tc = TC_GREEN; + } + DrawString(tr, text, tc); tr.top += this->line_height; } break; @@ -228,7 +236,7 @@ struct AIConfigWindow : public Window { case WID_AIC_LIST: { // Select a slot this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget); this->InvalidateData(); - if (click_count > 1 && this->selected_slot != INVALID_COMPANY) ShowScriptListWindow((CompanyID)this->selected_slot, _ctrl_pressed); + if (click_count > 1 && IsEditable(this->selected_slot)) ShowScriptListWindow((CompanyID)this->selected_slot, _ctrl_pressed); break; } @@ -258,7 +266,7 @@ struct AIConfigWindow : public Window { } case WID_AIC_CHANGE: // choose other AI - ShowScriptListWindow((CompanyID)this->selected_slot, _ctrl_pressed); + if (IsEditable(this->selected_slot)) ShowScriptListWindow((CompanyID)this->selected_slot, _ctrl_pressed); break; case WID_AIC_CONFIGURE: // change the settings for an AI @@ -282,7 +290,7 @@ struct AIConfigWindow : public Window { */ void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override { - if (!IsEditable(this->selected_slot)) { + if (!IsEditable(this->selected_slot) && !Company::IsValidAiID(this->selected_slot)) { this->selected_slot = INVALID_COMPANY; } @@ -294,10 +302,10 @@ struct AIConfigWindow : public Window { this->SetWidgetDisabledState(WID_AIC_INCREASE_NUMBER, GetGameSettings().difficulty.max_no_competitors == MAX_COMPANIES - 1); this->SetWidgetDisabledState(WID_AIC_DECREASE_INTERVAL, GetGameSettings().difficulty.competitors_interval == MIN_COMPETITORS_INTERVAL); this->SetWidgetDisabledState(WID_AIC_INCREASE_INTERVAL, GetGameSettings().difficulty.competitors_interval == MAX_COMPETITORS_INTERVAL); - this->SetWidgetDisabledState(WID_AIC_CHANGE, this->selected_slot == INVALID_COMPANY); + this->SetWidgetDisabledState(WID_AIC_CHANGE, !IsEditable(this->selected_slot)); this->SetWidgetDisabledState(WID_AIC_CONFIGURE, this->selected_slot == INVALID_COMPANY || config->GetConfigList()->empty()); - this->SetWidgetDisabledState(WID_AIC_MOVE_UP, this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot - 1))); - this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot + 1))); + this->SetWidgetDisabledState(WID_AIC_MOVE_UP, !IsEditable(this->selected_slot) || !IsEditable((CompanyID)(this->selected_slot - 1))); + this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, !IsEditable(this->selected_slot) || !IsEditable((CompanyID)(this->selected_slot + 1))); this->SetWidgetDisabledState(WID_AIC_OPEN_URL, this->selected_slot == INVALID_COMPANY || config->GetInfo() == nullptr || config->GetInfo()->GetURL().empty()); for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) { diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp index 6891ef81e6..ebdab9c58f 100644 --- a/src/ai/ai_info.cpp +++ b/src/ai/ai_info.cpp @@ -45,14 +45,14 @@ template <> const char *GetClassName() { return "AIInfo" SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting"); SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels"); SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_NONE"); - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_RANDOM, "CONFIG_RANDOM"); + SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_RANDOM"); // Deprecated, mapped to NONE. SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "CONFIG_BOOLEAN"); SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "CONFIG_INGAME"); SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_DEVELOPER, "CONFIG_DEVELOPER"); /* Pre 1.2 had an AI prefix */ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "AICONFIG_NONE"); - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_RANDOM, "AICONFIG_RANDOM"); + SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "AICONFIG_RANDOM"); // Deprecated, mapped to NONE. SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "AICONFIG_BOOLEAN"); SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "AICONFIG_INGAME"); diff --git a/src/command.cpp b/src/command.cpp index 3fd589a417..b81f732229 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -129,6 +129,7 @@ CommandProc CmdSetCompanyColour; CommandProc CmdIncreaseLoan; CommandProc CmdDecreaseLoan; +CommandProcEx CmdSetCompanyMaxLoan; CommandProc CmdWantEnginePreview; CommandProc CmdEngineCtrl; @@ -397,6 +398,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdIncreaseLoan, 0, CMDT_MONEY_MANAGEMENT ), // CMD_INCREASE_LOAN DEF_CMD(CmdDecreaseLoan, 0, CMDT_MONEY_MANAGEMENT ), // CMD_DECREASE_LOAN + DEF_CMD(CmdSetCompanyMaxLoan, CMD_DEITY, CMDT_MONEY_MANAGEMENT ), // CMD_SET_COMPANY_MAX_LOAN DEF_CMD(CmdWantEnginePreview, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_WANT_ENGINE_PREVIEW DEF_CMD(CmdEngineCtrl, CMD_DEITY, CMDT_VEHICLE_MANAGEMENT ), // CMD_ENGINE_CTRL @@ -732,7 +734,7 @@ char *DumpCommandLog(char *buffer, const char *last, std::functionmoney; -} - static void DebugLogCommandLogEntry(const CommandLogEntry &entry) { if (_debug_command_level <= 0) return; @@ -910,7 +898,7 @@ static void DebugLogCommandLogEntry(const CommandLogEntry &entry) char buffer[256]; char *b = buffer; DumpSubCommandLogEntry(b, lastof(buffer), entry); - debug_print("command", buffer); + debug_print("command", 0, buffer); } static void AppendCommandLogEntry(const CommandCost &res, TileIndex tile, uint32_t p1, uint32_t p2, uint64_t p3, uint32_t cmd, CommandLogEntryFlag log_flags, const char *text) @@ -949,7 +937,7 @@ static void AppendCommandLogEntry(const CommandCost &res, TileIndex tile, uint32 cmd_log.count++; } -/*! +/** * Toplevel network safe docommand function for the current company. Must not be called recursively. * The callback is called when the command succeeded or failed. The parameters * \a tile, \a p1, and \a p2 are from the #CommandProc function. The parameter \a cmd is the command to execute. @@ -1107,7 +1095,7 @@ void EnqueueDoCommandP(CommandContainer cmd) */ #define return_dcpi(cmd) { _docommand_recursive = 0; return cmd; } -/*! +/** * Helper function for the toplevel network safe docommand function for the current company. * * @param tile The tile to perform a command on (see #CommandProc) @@ -1206,7 +1194,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32_t p1, uint32_t p2, uint64_ std::string dbg_info = stdstr_fmt("%s: %s; company: %02x; tile: %06x (%u x %u); p1: %08x; p2: %08x; p3: " OTTD_PRINTFHEX64PAD "; cmd: %08x; %s <%s> (%s)", prefix, debug_date_dumper().HexDate(), (int)_current_company, tile, TileX(tile), TileY(tile), p1, p2, p3, cmd & ~CMD_NETWORK_COMMAND, text_buf.c_str(), aux_str.c_str(), GetCommandName(cmd)); - debug_print("desync", dbg_info.c_str()); + debug_print("desync", 1, dbg_info.c_str()); } }; diff --git a/src/command_func.h b/src/command_func.h index 04a6d7a95c..8ec82e00c7 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -65,7 +65,6 @@ extern Money _additional_cash_required; bool IsValidCommand(uint32_t cmd); CommandFlags GetCommandFlags(uint32_t cmd); const char *GetCommandName(uint32_t cmd); -Money GetAvailableMoneyForCommand(); bool IsCommandAllowedWhilePaused(uint32_t cmd); /** diff --git a/src/command_type.h b/src/command_type.h index 32c155b856..9d02060716 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -348,6 +348,7 @@ enum Commands { 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 bfa659e11b..3dbd5ad6f0 100644 --- a/src/company_base.h +++ b/src/company_base.h @@ -19,6 +19,8 @@ #include #include +static const Money COMPANY_MAX_LOAN_DEFAULT = INT64_MIN; + /** Statistics about the economy. */ struct CompanyEconomyEntry { Money income; ///< The amount of income. @@ -60,7 +62,6 @@ DECLARE_ENUM_AS_BIT_SET(CompanyBankruptcyFlags) typedef Pool CompanyPool; extern CompanyPool _company_pool; - /** Statically loadable part of Company pool item */ struct CompanyProperties { uint32_t name_2; ///< Parameter of #name_1. @@ -76,6 +77,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. @@ -120,8 +122,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), share_owners(), 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), share_owners(), inaugurated_year(0), months_of_bankruptcy(0), bankrupt_last_asked(INVALID_COMPANY), bankrupt_flags(CBRF_NONE), bankrupt_asked(0), bankrupt_timeout(0), bankrupt_value(0), terraform_limit(0), clear_limit(0), tree_limit(0), purchase_land_limit(0), build_object_limit(0), is_ai(false), engine_renew_list(nullptr) {} }; @@ -141,6 +143,8 @@ struct Company : CompanyPool::PoolItem<&_company_pool>, CompanyProperties { 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 6cdc9902f5..b9be67b7b3 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -109,6 +109,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. @@ -216,20 +226,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 f973c15fa1..14a1eae72a 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/company_gui.cpp b/src/company_gui.cpp index 06950bd48c..fc56f07edb 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -373,9 +373,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: @@ -473,7 +475,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/debug.cpp b/src/debug.cpp index f026cad7ba..cbbda99dd9 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -36,8 +36,9 @@ /** Element in the queue of debug messages that have to be passed to either NetworkAdminConsole or IConsolePrint.*/ struct QueuedDebugItem { - std::string level; ///< The used debug level. - std::string message; ///< The actual formatted message. + std::string category; ///< The used debug category. + int level; ///< The used debug level. + std::string message; ///< The actual formatted message. }; std::atomic _debug_remote_console; ///< Whether we need to send data to either NetworkAdminConsole or IConsolePrint. std::mutex _debug_remote_console_mutex; ///< Mutex to guard the queue of debug messages for either NetworkAdminConsole or IConsolePrint. @@ -136,15 +137,16 @@ char *DumpDebugFacilityNames(char *buf, char *last) /** * Internal function for outputting the debug line. * @param dbg Debug category. + * @param level Debug level. * @param buf Text line to output. */ -void debug_print(const char *dbg, const char *buf) +void debug_print(const char *dbg, int level, const char *buf) { if (strcmp(dbg, "desync") == 0) { static FILE *f = FioFOpenFile("commands-out.log", "wb", AUTOSAVE_DIR); if (f != nullptr) { - fprintf(f, "%s%s\n", log_prefix().GetLogPrefix(), buf); + fprintf(f, "%s%s\n", log_prefix().GetLogPrefix(true), buf); fflush(f); } #ifdef RANDOM_DEBUG @@ -178,7 +180,7 @@ void debug_print(const char *dbg, const char *buf) } char buffer[512]; - seprintf(buffer, lastof(buffer), "%sdbg: [%s] %s\n", log_prefix().GetLogPrefix(), dbg, buf); + seprintf(buffer, lastof(buffer), "%sdbg: [%s:%d] %s\n", log_prefix().GetLogPrefix(), dbg, level, buf); str_strip_colours(buffer); @@ -197,10 +199,10 @@ void debug_print(const char *dbg, const char *buf) /* Only add to the queue when there is at least one consumer of the data. */ if (IsNonGameThread()) { std::lock_guard lock(_debug_remote_console_mutex); - _debug_remote_console_queue.push_back({ dbg, buf }); + _debug_remote_console_queue.push_back({ dbg, level, buf }); } else { NetworkAdminConsole(dbg, buf); - if (_settings_client.gui.developer >= 2) IConsolePrintF(CC_DEBUG, "dbg: [%s] %s", dbg, buf); + if (_settings_client.gui.developer >= 2) IConsolePrintF(CC_DEBUG, "dbg: [%s:%d] %s", dbg, level, buf); } } } @@ -211,7 +213,7 @@ void debug_print(const char *dbg, const char *buf) * @param dbg Debug category. * @param format Text string a la printf, with optional arguments. */ -void CDECL debug(const char *dbg, const char *format, ...) +void CDECL debug(const char *dbg, int level, const char *format, ...) { char buf[1024]; @@ -220,7 +222,7 @@ void CDECL debug(const char *dbg, const char *format, ...) vseprintf(buf, lastof(buf), format, va); va_end(va); - debug_print(dbg, buf); + debug_print(dbg, level, buf); } /** @@ -306,13 +308,16 @@ std::string GetDebugString() } /** - * Get the prefix for logs; if show_date_in_logs is enabled it returns - * the date, otherwise it returns nothing. - * @return the prefix for logs (do not free), never nullptr + * Get the prefix for logs. + * + * If show_date_in_logs or \p force is enabled it returns + * the date, otherwise it returns an empty string. + * + * @return the prefix for logs (do not free), never nullptr. */ -const char *log_prefix::GetLogPrefix() +const char *log_prefix::GetLogPrefix(bool force) { - if (_settings_client.gui.show_date_in_logs) { + if (force || _settings_client.gui.show_date_in_logs) { LocalTime::Format(this->buffer, lastof(this->buffer), "[%Y-%m-%d %H:%M:%S] "); } else { this->buffer[0] = '\0'; @@ -430,8 +435,8 @@ void DebugSendRemoteMessages() } for (auto &item : _debug_remote_console_queue_spare) { - NetworkAdminConsole(item.level.c_str(), item.message.c_str()); - if (_settings_client.gui.developer >= 2) IConsolePrintF(CC_DEBUG, "dbg: [%s] %s", item.level.c_str(), item.message.c_str()); + NetworkAdminConsole(item.category.c_str(), item.message.c_str()); + if (_settings_client.gui.developer >= 2) IConsolePrintF(CC_DEBUG, "dbg: [%s:%d] %s", item.category.c_str(), item.level, item.message.c_str()); } _debug_remote_console_queue_spare.clear(); diff --git a/src/debug.h b/src/debug.h index 4e11f2e41f..51232533fa 100644 --- a/src/debug.h +++ b/src/debug.h @@ -33,7 +33,7 @@ * @param name Category * @param level Debugging level, higher levels means more detailed information. */ -#define DEBUG(name, level, ...) if ((level) == 0 || _debug_ ## name ## _level >= (level)) debug(#name, __VA_ARGS__) +#define DEBUG(name, level, ...) do { if ((level) == 0 || _debug_ ## name ## _level >= (level)) debug(#name, level, __VA_ARGS__); } while (false) extern int _debug_driver_level; extern int _debug_grf_level; @@ -64,8 +64,8 @@ extern std::string _loadgame_DBGL_data; extern bool _save_DBGC_data; extern std::string _loadgame_DBGC_data; -void CDECL debug(const char *dbg, const char *format, ...) WARN_FORMAT(2, 3); -void debug_print(const char *dbg, const char *buf); +void CDECL debug(const char *dbg, int level, const char *format, ...) WARN_FORMAT(3, 4); +void debug_print(const char *dbg, int level, const char *buf); char *DumpDebugFacilityNames(char *buf, char *last); void SetDebugString(const char *s, void (*error_func)(const char *)); @@ -117,7 +117,7 @@ inline void ShowInfoI(const std::string &str) } struct log_prefix { - const char *GetLogPrefix(); + const char *GetLogPrefix(bool force = false); private: char buffer[24]; diff --git a/src/debug_fmt.h b/src/debug_fmt.h index d4d4851c17..b2a81c524d 100644 --- a/src/debug_fmt.h +++ b/src/debug_fmt.h @@ -21,7 +21,7 @@ * @param level The maximum debug level this message should be shown at. When the debug level for this category is set lower, then the message will not be shown. * @param format_string The formatting string of the message. */ -#define Debug(name, level, format_string, ...) if ((level) == 0 || _debug_ ## name ## _level >= (level)) debug_print(#name, fmt::format(FMT_STRING(format_string), ## __VA_ARGS__).c_str()) +#define Debug(name, level, format_string, ...) do { if ((level) == 0 || _debug_ ## name ## _level >= (level)) debug_print(#name, level, fmt::format(FMT_STRING(format_string), ## __VA_ARGS__).c_str()); } while (false) void NORETURN usererror_str(const char *msg); void NORETURN fatalerror_str(const char *msg); diff --git a/src/economy.cpp b/src/economy.cpp index 732400336a..6da700532c 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -680,9 +680,12 @@ 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->bankrupt_flags & CBRF_SALE) return; - 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; @@ -952,17 +955,21 @@ static void CompaniesPayInterest() /* Over a year the paid interest should be "loan * interest percentage", * but... as that number is likely not dividable by 12 (pay each month), * one needs to account for that in the monthly fee calculations. + * * To easily calculate what one should pay "this" month, you calculate * what (total) should have been paid up to this month and you subtract * whatever has been paid in the previous months. This will mean one month * it'll be a bit more and the other it'll be a bit less than the average * monthly fee, but on average it will be exact. + * * In order to prevent cheating or abuse (just not paying interest by not - * taking a loan we make companies pay interest on negative cash as well + * taking a loan) we make companies pay interest on negative cash as well, + * except if infinite money is enabled. */ Money yearly_fee = c->current_loan * _economy.interest_rate / 100; - if (c->money < 0) { - yearly_fee += -c->money *_economy.interest_rate / 100; + Money available_money = GetAvailableMoney(c->index); + if (available_money < 0) { + yearly_fee += -available_money * _economy.interest_rate / 100; } Money up_to_previous_month = yearly_fee * EconTime::CurMonth() / 12; Money up_to_this_month = yearly_fee * (EconTime::CurMonth() + 1) / 12; diff --git a/src/economy_type.h b/src/economy_type.h index 4f1e04a183..3fc05f1907 100644 --- a/src/economy_type.h +++ b/src/economy_type.h @@ -205,6 +205,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/engine.cpp b/src/engine.cpp index e6ca5a898c..700115ab26 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -85,6 +85,8 @@ Engine::Engine(VehicleType type, EngineID base) if (type == VEH_ROAD) this->u.road.tractive_effort = 0x4C; /* Aircraft must have INVALID_CARGO as default, as there is no property */ if (type == VEH_AIRCRAFT) this->info.cargo_type = INVALID_CARGO; + /* Ships must have a non-zero acceleration. */ + if (type == VEH_SHIP) this->u.ship.acceleration = 1; /* Set visual effect to the default value */ switch (type) { case VEH_TRAIN: this->u.rail.visual_effect = VE_DEFAULT; break; diff --git a/src/engine_type.h b/src/engine_type.h index b0b0fc8251..f0c09ccb53 100644 --- a/src/engine_type.h +++ b/src/engine_type.h @@ -67,6 +67,7 @@ struct RailVehicleInfo { struct ShipVehicleInfo { byte image_index; byte cost_factor; + uint8_t acceleration; ///< Acceleration (1 unit = 1/3.2 mph per tick = 0.5 km-ish/h per tick) uint16_t max_speed; ///< Maximum speed (1 unit = 1/3.2 mph = 0.5 km-ish/h) uint16_t capacity; byte running_cost; diff --git a/src/error_gui.cpp b/src/error_gui.cpp index 44164fa169..6b2357550a 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -211,26 +211,20 @@ public: return pt; } - /* Find the free screen space between the main toolbar at the top, and the statusbar at the bottom. - * Add a fixed distance 20 to make it less cluttered. - */ - int scr_top = GetMainViewTop() + 20; - int scr_bot = GetMainViewBottom() - 20; - - Point pt = RemapCoords(this->position.x, this->position.y, GetSlopePixelZOutsideMap(this->position.x, this->position.y)); - const Viewport *vp = GetMainWindow()->viewport; - if (this->face == INVALID_COMPANY) { - /* move x pos to opposite corner */ - pt.x = UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left; - pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - sm_width - 20 : 20; // Stay 20 pixels away from the edge of the screen. - - /* move y pos to opposite corner */ - pt.y = UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top; - pt.y = (pt.y < (_screen.height >> 1)) ? scr_bot - sm_height : scr_top; - } else { - pt.x = std::min(std::max(UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left - (sm_width / 2), 0), _screen.width - sm_width); - pt.y = std::min(std::max(UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top - (sm_height / 2), scr_top), scr_bot - sm_height); + constexpr int distance_to_cursor = 200; + + /* Position the error window just above the cursor. This makes the + * error window clearly visible, without being in the way of what + * the user is doing. */ + Point pt; + pt.x = _cursor.pos.x - sm_width / 2; + pt.y = _cursor.pos.y - (distance_to_cursor + sm_height); + + if (pt.y < GetMainViewTop()) { + /* Window didn't fit above cursor, so place it below. */ + pt.y = _cursor.pos.y + distance_to_cursor; } + return pt; } diff --git a/src/game/game.hpp b/src/game/game.hpp index 3c8fcdad92..1ae0387f7e 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -31,8 +31,9 @@ public: /** * Start up a new GameScript. + * @param randomise Whether to randomise the configured GameScript. */ - static void StartNew(); + static void StartNew(bool randomise = true); /** * Uninitialize the Game system. diff --git a/src/game/game_core.cpp b/src/game/game_core.cpp index 7164d40009..af922ac5df 100644 --- a/src/game/game_core.cpp +++ b/src/game/game_core.cpp @@ -69,7 +69,7 @@ } } -/* static */ void Game::StartNew() +/* static */ void Game::StartNew(bool randomise) { if (Game::instance != nullptr) return; @@ -83,6 +83,7 @@ GameInfo *info = config->GetInfo(); if (info == nullptr) return; + if (randomise) config->AddRandomDeviation(); config->AnchorUnchangeableSettings(); Backup cur_company(_current_company, FILE_LINE); diff --git a/src/game/game_gui.cpp b/src/game/game_gui.cpp index 0d6302795f..a2f8f34646 100644 --- a/src/game/game_gui.cpp +++ b/src/game/game_gui.cpp @@ -206,10 +206,18 @@ struct GSConfigWindow : public Window { TextColour colour; uint idx = 0; if (config_item.description.empty()) { - str = STR_JUST_STRING1; + if (Game::GetInstance() == nullptr && config_item.random_deviation != 0) { + str = STR_AI_SETTINGS_JUST_DEVIATION; + } else { + str = STR_JUST_STRING1; + } colour = TC_ORANGE; } else { - str = STR_AI_SETTINGS_SETTING; + if (Game::GetInstance() == nullptr && config_item.random_deviation != 0) { + str = STR_AI_SETTINGS_SETTING_DEVIATION; + } else { + str = STR_AI_SETTINGS_SETTING; + } colour = TC_LIGHT_BLUE; SetDParamStr(idx++, config_item.description); } @@ -223,13 +231,36 @@ struct GSConfigWindow : public Window { } else { DrawArrowButtons(br.left, y + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, editable && current_value > config_item.min_value, editable && current_value < config_item.max_value); } - auto config_iterator = config_item.labels.find(current_value); - if (config_iterator != config_item.labels.end()) { - SetDParam(idx++, STR_JUST_RAW_STRING); - SetDParamStr(idx++, config_iterator->second); + + if (Game::GetInstance() != nullptr || config_item.random_deviation == 0) { + auto config_iterator = config_item.labels.find(current_value); + if (config_iterator != config_item.labels.end()) { + SetDParam(idx++, STR_JUST_RAW_STRING); + SetDParamStr(idx++, config_iterator->second); + } else { + SetDParam(idx++, STR_JUST_INT); + SetDParam(idx++, current_value); + } } else { - SetDParam(idx++, STR_JUST_INT); - SetDParam(idx++, current_value); + int min_deviated = std::max(config_item.min_value, current_value - config_item.random_deviation); + auto config_iterator = config_item.labels.find(min_deviated); + if (config_iterator != config_item.labels.end()) { + SetDParam(idx++, STR_JUST_RAW_STRING); + SetDParamStr(idx++, config_iterator->second); + } else { + SetDParam(idx++, STR_JUST_INT); + SetDParam(idx++, min_deviated); + } + + int max_deviated = std::min(config_item.max_value, current_value + config_item.random_deviation); + config_iterator = config_item.labels.find(max_deviated); + if (config_iterator != config_item.labels.end()) { + SetDParam(idx++, STR_JUST_RAW_STRING); + SetDParamStr(idx++, config_iterator->second); + } else { + SetDParam(idx++, STR_JUST_INT); + SetDParam(idx++, max_deviated); + } } } diff --git a/src/game/game_info.cpp b/src/game/game_info.cpp index e64a7a1de1..9b5debfb83 100644 --- a/src/game/game_info.cpp +++ b/src/game/game_info.cpp @@ -42,7 +42,7 @@ template <> const char *GetClassName() { return "GSInf SQGSInfo.DefSQAdvancedMethod(engine, &GameInfo::AddSetting, "AddSetting"); SQGSInfo.DefSQAdvancedMethod(engine, &GameInfo::AddLabels, "AddLabels"); SQGSInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_NONE"); - SQGSInfo.DefSQConst(engine, SCRIPTCONFIG_RANDOM, "CONFIG_RANDOM"); + SQGSInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_RANDOM"); // Deprecated, mapped to NONE. SQGSInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "CONFIG_BOOLEAN"); SQGSInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "CONFIG_INGAME"); SQGSInfo.DefSQConst(engine, SCRIPTCONFIG_DEVELOPER, "CONFIG_DEVELOPER"); diff --git a/src/genworld.cpp b/src/genworld.cpp index 6915563706..22a911d759 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -325,7 +325,7 @@ void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_setti _settings_game.construction.map_height_limit = std::max(MAP_HEIGHT_LIMIT_AUTO_MINIMUM, std::min(MAX_MAP_HEIGHT_LIMIT, estimated_height + MAP_HEIGHT_LIMIT_AUTO_CEILING_ROOM)); } - if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = _settings_newgame.game_creation.generation_seed = InteractiveRandom(); + if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = InteractiveRandom(); /* Load the right landscape stuff, and the NewGRFs! */ GfxLoadSprites(); diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index d838994306..e78d5acdc4 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -37,11 +37,14 @@ static CompanyMask _legend_excluded_companies; static CargoTypes _legend_excluded_cargo; +uint8_t _cargo_payment_x_mode; + /* Apparently these don't play well with enums. */ static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn. static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn. -uint8_t _cargo_payment_x_mode; +constexpr double INT64_MAX_IN_DOUBLE = static_cast(INT64_MAX - 512); ///< The biggest double that when cast to int64_t still fits in a int64_t. +static_assert(static_cast(INT64_MAX_IN_DOUBLE) < INT64_MAX); /****************/ /* GRAPH LEGEND */ @@ -227,16 +230,16 @@ protected: } } - /* Prevent showing values too close to the graph limits. */ - current_interval.highest = (11 * current_interval.highest) / 10; - current_interval.lowest = (11 * current_interval.lowest) / 10; - /* Always include zero in the shown range. */ double abs_lower = (current_interval.lowest > 0) ? 0 : (double)abs(current_interval.lowest); double abs_higher = (current_interval.highest < 0) ? 0 : (double)current_interval.highest; + /* Prevent showing values too close to the graph limits. */ + abs_higher = (11.0 * abs_higher) / 10.0; + abs_lower = (11.0 * abs_lower) / 10.0; + int num_pos_grids; - int64_t grid_size; + OverflowSafeInt64 grid_size; if (abs_lower != 0 || abs_higher != 0) { /* The number of grids to reserve for the positive part is: */ @@ -247,8 +250,19 @@ protected: if (num_pos_grids == num_hori_lines && abs_lower != 0) num_pos_grids--; /* Get the required grid size for each side and use the maximum one. */ - int64_t grid_size_higher = (abs_higher > 0) ? ((int64_t)abs_higher + num_pos_grids - 1) / num_pos_grids : 0; - int64_t grid_size_lower = (abs_lower > 0) ? ((int64_t)abs_lower + num_hori_lines - num_pos_grids - 1) / (num_hori_lines - num_pos_grids) : 0; + + OverflowSafeInt64 grid_size_higher = 0; + if (abs_higher > 0) { + grid_size_higher = abs_higher > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast(abs_higher); + grid_size_higher = (grid_size_higher + num_pos_grids - 1) / num_pos_grids; + } + + OverflowSafeInt64 grid_size_lower = 0; + if (abs_lower > 0) { + grid_size_lower = abs_lower > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast(abs_lower); + grid_size_lower = (grid_size_lower + num_hori_lines - num_pos_grids - 1) / (num_hori_lines - num_pos_grids); + } + grid_size = std::max(grid_size_higher, grid_size_lower); } else { /* If both values are zero, show an empty graph. */ @@ -637,7 +651,13 @@ public: if (c != nullptr) { this->colours[numd] = _colour_gradient[c->colour][6]; for (int j = this->num_on_x_axis, i = 0; --j >= 0;) { - this->cost[numd][i] = (j >= c->num_valid_stat_ent) ? INVALID_DATAPOINT : GetGraphData(c, j); + if (j >= c->num_valid_stat_ent) { + this->cost[numd][i] = INVALID_DATAPOINT; + } else { + /* Ensure we never assign INVALID_DATAPOINT, as that has another meaning. + * Instead, use the value just under it. Hopefully nobody will notice. */ + this->cost[numd][i] = std::min(GetGraphData(c, j), INVALID_DATAPOINT - 1); + } i++; } } diff --git a/src/misc_cmd.cpp b/src/misc_cmd.cpp index dda8342038..4e1bbf866e 100644 --- a/src/misc_cmd.cpp +++ b/src/misc_cmd.cpp @@ -46,9 +46,9 @@ static_assert((LOAN_INTERVAL & 3) == 0); CommandCost CmdIncreaseLoan(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, const char *text) { 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); } @@ -59,11 +59,11 @@ CommandCost CmdIncreaseLoan(TileIndex tile, DoCommandFlag flags, uint32_t p1, ui loan = LOAN_INTERVAL; break; case 1: // Take a loan as big as possible - loan = _economy.max_loan - c->current_loan; + loan = max_loan - c->current_loan; break; case 2: // Take the given amount of loan loan = ((uint64_t)p1 << 32) | (p2 & 0xFFFFFFFC); - 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; } @@ -106,7 +106,7 @@ CommandCost CmdDecreaseLoan(TileIndex tile, DoCommandFlag flags, uint32_t p1, ui loan = std::min(c->current_loan, (Money)LOAN_INTERVAL); break; case 1: // Pay back as much as possible - loan = std::max(std::min(c->current_loan, c->money), (Money)LOAN_INTERVAL); + loan = std::max(std::min(c->current_loan, GetAvailableMoneyForCommand()), (Money)LOAN_INTERVAL); loan -= loan % LOAN_INTERVAL; break; case 2: // Repay the given amount of loan @@ -115,7 +115,7 @@ CommandCost CmdDecreaseLoan(TileIndex tile, DoCommandFlag flags, uint32_t p1, ui break; } - if (c->money < loan) { + if (GetAvailableMoneyForCommand() < loan) { SetDParam(0, loan); return_cmd_error(STR_ERROR_CURRENCY_REQUIRED); } @@ -128,6 +128,38 @@ CommandCost CmdDecreaseLoan(TileIndex tile, DoCommandFlag flags, uint32_t p1, ui return CommandCost(); } +/** + * Sets the max loan amount of your company. Does not respect the global loan setting. + * @param tile unused + * @param flags operation to perform + * @param p1 the company ID. + * @param p2 unused + * @param p3 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. + * @param text unused + * @return zero cost or an error + */ +CommandCost CmdSetCompanyMaxLoan(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, uint64_t p3, const char *text, const CommandAuxiliaryBase *aux_data) +{ + if (_current_company != OWNER_DEITY) return CMD_ERROR; + + Money amount = (Money)p3; + if (amount != COMPANY_MAX_LOAN_DEFAULT) { + if (amount < 0 || amount > (Money)MAX_LOAN_LIMIT) return CMD_ERROR; + } + + Company *c = Company::GetIfValid((CompanyID)p1); + 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/network/core/http_winhttp.cpp b/src/network/core/http_winhttp.cpp index 7bb9dee806..b326be8445 100644 --- a/src/network/core/http_winhttp.cpp +++ b/src/network/core/http_winhttp.cpp @@ -73,15 +73,16 @@ NetworkHTTPRequest::NetworkHTTPRequest(const std::wstring &uri, HTTPCallback *ca static std::string GetLastErrorAsString() { - char buffer[512]; + wchar_t buffer[512]; + DWORD error_code = GetLastError(); - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandleA("winhttp.dll"), error_code, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), nullptr) == 0) { + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle(L"winhttp.dll"), error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, lengthof(buffer), nullptr) == 0) { return fmt::format("unknown error {}", error_code); } - return buffer; + return FS2OTTD(buffer); } /** diff --git a/src/network/core/os_abstraction.cpp b/src/network/core/os_abstraction.cpp index 2cdbc9fd9f..8d3349392f 100644 --- a/src/network/core/os_abstraction.cpp +++ b/src/network/core/os_abstraction.cpp @@ -80,12 +80,15 @@ const char *NetworkError::AsString() const { if (this->message.empty()) { #if defined(_WIN32) - char buffer[512]; - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, this->error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), nullptr) == 0) { - seprintf(buffer, lastof(buffer), "Unknown error %d", this->error); + wchar_t buffer[512]; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, this->error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, lengthof(buffer), nullptr) == 0) { + char errbuffer[32]; + seprintf(errbuffer, lastof(errbuffer), "Unknown error %d", this->error); + this->message.assign(errbuffer); + } else { + this->message.assign(FS2OTTD(buffer)); } - this->message.assign(buffer); #else /* Make strerror thread safe by locking access to it. There is a thread safe strerror_r, however * the non-POSIX variant is available due to defining _GNU_SOURCE meaning it is not portable. diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 6360702732..4b9dc4fdf9 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1688,6 +1688,18 @@ public: void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override { switch (widget) { + case WID_CL_SERVER_NAME: + case WID_CL_CLIENT_NAME: + if (widget == WID_CL_SERVER_NAME) { + SetDParamStr(0, _network_server ? _settings_client.network.server_name : _network_server_name); + } else { + const NetworkClientInfo *own_ci = NetworkClientInfo::GetByClientID(_network_own_client_id); + SetDParamStr(0, own_ci != nullptr ? own_ci->client_name : _settings_client.network.client_name); + } + *size = GetStringBoundingBox(STR_JUST_RAW_STRING); + size->width = std::min(size->width, static_cast(ScaleGUITrad(200))); // By default, don't open the window too wide. + break; + case WID_CL_SERVER_VISIBILITY: *size = maxdim(maxdim(GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_LOCAL), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_PUBLIC)), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY)); size->width += padding.width; diff --git a/src/newgrf.cpp b/src/newgrf.cpp index a9f240e6c4..e1a7b0b166 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1542,7 +1542,7 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop svi->cost_factor = buf->ReadByte(); break; - case PROP_SHIP_SPEED: // 0x0B Speed (1 unit is 0.5 km-ish/h) + case PROP_SHIP_SPEED: // 0x0B Speed (1 unit is 0.5 km-ish/h). Use 0x23 to achieve higher speeds. svi->max_speed = buf->ReadByte(); break; @@ -1671,6 +1671,14 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop SB(ei->callback_mask, 8, 8, buf->ReadByte()); break; + case 0x23: // Speed (1 unit is 0.5 km-ish/h) + svi->max_speed = buf->ReadWord(); + break; + + case 0x24: // Acceleration (1 unit is 0.5 km-ish/h per tick) + svi->acceleration = std::max(1, buf->ReadByte()); + break; + default: ret = CommonVehicleChangeInfo(ei, prop, mapping_entry, buf); break; diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index 41c7f1300d..fffe0f8ae3 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/openttd.cpp b/src/openttd.cpp index 1b69d240d5..51559beb8f 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -678,7 +678,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { SetEffectVolume(_settings_client.music.effect_vol); if (startyear != CalTime::INVALID_YEAR) IConsoleSetSetting("game_creation.starting_year", startyear.base()); - if (generation_seed != GENERATE_NEW_SEED) _settings_newgame.game_creation.generation_seed = generation_seed; + _settings_newgame.game_creation.generation_seed = generation_seed; if (!dedicated_host.empty()) { _network_bind_list.clear(); diff --git a/src/os/windows/library_loader_win.cpp b/src/os/windows/library_loader_win.cpp index 838a2117f4..feb90b0629 100644 --- a/src/os/windows/library_loader_win.cpp +++ b/src/os/windows/library_loader_win.cpp @@ -20,13 +20,13 @@ static std::string GetLoadError() { auto error_code = GetLastError(); - char buffer[512]; - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), nullptr) == 0) { + wchar_t buffer[512]; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, lengthof(buffer), nullptr) == 0) { return fmt::format("Unknown error {}", error_code); } - return buffer; + return FS2OTTD(buffer); } void *LibraryLoader::OpenLibrary(const std::string &filename) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index c45014329a..5f28637b2b 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -3957,7 +3957,9 @@ void DrawTrackBits(TileInfo *ti, TrackBits track) static void DrawSignals(TileIndex tile, TrackBits rails, const RailTypeInfo *rti) { -#define MAYBE_DRAW_SIGNAL(x, y, z, t) if (IsSignalPresent(tile, x)) DrawSingleSignal(tile, rti, t, GetSingleSignalState(tile, x), y, z) + auto MAYBE_DRAW_SIGNAL = [&](byte signalbit, SignalOffsets image, uint pos, Track track) { + if (IsSignalPresent(tile, signalbit)) DrawSingleSignal(tile, rti, track, GetSingleSignalState(tile, signalbit), image, pos); + }; if (!(rails & TRACK_BIT_Y)) { if (!(rails & TRACK_BIT_X)) { diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index c7b12c6b97..5580b2d0b2 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -13,6 +13,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/saveload/afterload.cpp b/src/saveload/afterload.cpp index ddfad4ad19..edb25f4577 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -653,7 +653,7 @@ static void StartScripts() } /* Start the GameScript. */ - Game::StartNew(); + Game::StartNew(false); ShowScriptDebugWindowIfScriptError(); } @@ -4360,6 +4360,19 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_SHIP_ACCELERATION) && SlXvIsFeatureMissing(XSLFI_SHIP_ACCELERATION)) { + /* NewGRF acceleration information was added to ships. */ + for (Ship *s : Ship::Iterate()) { + if (s->acceleration == 0) s->acceleration = ShipVehInfo(s->engine_type)->acceleration; + } + } + + 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 cd3c39a27d..d21efa76c2 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -249,6 +249,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/script/api/ai_changelog.hpp b/src/script/api/ai_changelog.hpp index 6af41d52a4..4bc5b5d18b 100644 --- a/src/script/api/ai_changelog.hpp +++ b/src/script/api/ai_changelog.hpp @@ -24,6 +24,7 @@ * * API removals: * \li AIError::ERR_PRECONDITION_TOO_MANY_PARAMETERS, that error is never returned anymore. + * \li AIInfo::CONFIG_RANDOM, no longer used. * * Other changes: * \li AIGroupList accepts an optional filter function diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index 71ff4b6d5a..44d9ac0308 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 @@ -88,6 +90,7 @@ * * API removals: * \li GSError::ERR_PRECONDITION_TOO_MANY_PARAMETERS, that error is never returned anymore. + * \li AIInfo::CONFIG_RANDOM, no longer used. * * Other changes: * \li GSGroupList accepts an optional filter function diff --git a/src/script/api/script_company.cpp b/src/script/api/script_company.cpp index a329bcd576..1548719739 100644 --- a/src/script/api/script_company.cpp +++ b/src/script/api/script_company.cpp @@ -186,7 +186,7 @@ company = ResolveCompanyID(company); if (company == COMPANY_INVALID) return -1; - return ::Company::Get(company)->money; + return GetAvailableMoney((::CompanyID)company); } /* static */ Money ScriptCompany::GetLoanAmount() @@ -199,7 +199,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::DoCommandEx(0, company, 0, (uint64_t)amount, CMD_SET_COMPANY_MAX_LOAN); +} + +/* static */ bool ScriptCompany::ResetMaxLoanAmountForCompany(CompanyID company) +{ + EnforceDeityMode(false); + + company = ResolveCompanyID(company); + EnforcePrecondition(false, company != COMPANY_INVALID); + + return ScriptObject::DoCommandEx(0, company, 0, (uint64_t)COMPANY_MAX_LOAN_DEFAULT, CMD_SET_COMPANY_MAX_LOAN); } /* static */ Money ScriptCompany::GetLoanInterval() diff --git a/src/script/api/script_company.hpp b/src/script/api/script_company.hpp index e4efa6fb4e..ada4893366 100644 --- a/src/script/api/script_company.hpp +++ b/src/script/api/script_company.hpp @@ -219,12 +219,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/script/api/script_info_docs.hpp b/src/script/api/script_info_docs.hpp index 902fc7357a..7a884dc824 100644 --- a/src/script/api/script_info_docs.hpp +++ b/src/script/api/script_info_docs.hpp @@ -203,7 +203,6 @@ public: /** Miscellaneous flags for Script settings. */ enum ScriptConfigFlags { CONFIG_NONE, ///< Normal setting. - CONFIG_RANDOM, ///< When randomizing the Script, pick any value between min_value and max_value (inclusive). CONFIG_BOOLEAN, ///< This value is a boolean (either 0 (false) or 1 (true) ). CONFIG_INGAME, ///< This setting can be changed while the Script is running. CONFIG_DEVELOPER, ///< This setting will only be visible when the Script development tools are active. @@ -236,10 +235,11 @@ public: * is selected. Required. The value will be clamped in the range * [MIN(int32_t), MAX(int32_t)] (inclusive). * - random_deviation If this property has a nonzero value, then the - * actual value of the setting in game will be randomized in the range + * actual value of the setting in game will be randomised in the range * [user_configured_value - random_deviation, user_configured_value + random_deviation] (inclusive). * random_deviation sign is ignored and the value is clamped in the range [0, MAX(int32_t)] (inclusive). - * Not allowed if the CONFIG_RANDOM flag is set, otherwise optional. + * The randomisation will happen just before the Script start. + * Not allowed if the CONFIG_BOOLEAN flag is set, otherwise optional. * - step_size The increase/decrease of the value every time the user * clicks one of the up/down arrow buttons. Optional, default is 1. * - flags Bitmask of some flags, see ScriptConfigFlags. Required. diff --git a/src/script/script_config.cpp b/src/script/script_config.cpp index cb3478e5d2..3c2adedb93 100644 --- a/src/script/script_config.cpp +++ b/src/script/script_config.cpp @@ -33,18 +33,6 @@ void ScriptConfig::Change(std::optional name, int version, bo this->to_load_data.reset(); this->ClearConfigList(); - - if (_game_mode == GM_NORMAL && this->info != nullptr) { - /* If we're in an existing game and the Script is changed, set all settings - * for the Script that have the random flag to a random value. */ - for (const auto &item : *this->info->GetConfigList()) { - if (item.flags & SCRIPTCONFIG_RANDOM) { - this->SetSetting(item.name, ScriptObject::GetRandomizer(OWNER_NONE).Next(item.max_value + 1 - item.min_value) + item.min_value); - } - } - - this->AddRandomDeviation(); - } } ScriptConfig::ScriptConfig(const ScriptConfig *config) @@ -58,9 +46,6 @@ ScriptConfig::ScriptConfig(const ScriptConfig *config) for (const auto &item : config->settings) { this->settings[item.first] = item.second; } - - /* Virtual functions get called statically in constructors, so make it explicit to remove any confusion. */ - this->ScriptConfig::AddRandomDeviation(); } ScriptConfig::~ScriptConfig() diff --git a/src/script/script_config.hpp b/src/script/script_config.hpp index f15cae2faf..ab9a7def51 100644 --- a/src/script/script_config.hpp +++ b/src/script/script_config.hpp @@ -23,7 +23,7 @@ static const int INT32_DIGITS_WITH_SIGN_AND_TERMINATION = 10 + 1 + 1; /** Bitmask of flags for Script settings. */ enum ScriptConfigFlags { SCRIPTCONFIG_NONE = 0x0, ///< No flags set. - SCRIPTCONFIG_RANDOM = 0x1, ///< When randomizing the Script, pick any value between min_value and max_value when on custom difficulty setting. + // Unused flag 0x1. SCRIPTCONFIG_BOOLEAN = 0x2, ///< This value is a boolean (either 0 (false) or 1 (true) ). SCRIPTCONFIG_INGAME = 0x4, ///< This setting can be changed while the Script is running. SCRIPTCONFIG_DEVELOPER = 0x8, ///< This setting will only be visible when the Script development tools are active. diff --git a/src/script/script_gui.cpp b/src/script/script_gui.cpp index 7a0f192cfb..d209aa396b 100644 --- a/src/script/script_gui.cpp +++ b/src/script/script_gui.cpp @@ -395,10 +395,18 @@ struct ScriptSettingsWindow : public Window { TextColour colour; uint idx = 0; if (config_item.description.empty()) { - str = STR_JUST_STRING1; + if (this->slot != OWNER_DEITY && !Company::IsValidID(this->slot) && config_item.random_deviation != 0) { + str = STR_AI_SETTINGS_JUST_DEVIATION; + } else { + str = STR_JUST_STRING1; + } colour = TC_ORANGE; } else { - str = STR_AI_SETTINGS_SETTING; + if (this->slot != OWNER_DEITY && !Company::IsValidID(this->slot) && config_item.random_deviation != 0) { + str = STR_AI_SETTINGS_SETTING_DEVIATION; + } else { + str = STR_AI_SETTINGS_SETTING; + } colour = TC_LIGHT_BLUE; SetDParamStr(idx++, config_item.description); } @@ -413,13 +421,35 @@ struct ScriptSettingsWindow : public Window { DrawArrowButtons(br.left, y + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, editable && current_value > config_item.min_value, editable && current_value < config_item.max_value); } - auto config_iterator = config_item.labels.find(current_value); - if (config_iterator != config_item.labels.end()) { - SetDParam(idx++, STR_JUST_RAW_STRING); - SetDParamStr(idx++, config_iterator->second); + if (this->slot == OWNER_DEITY || Company::IsValidID(this->slot) || config_item.random_deviation == 0) { + auto config_iterator = config_item.labels.find(current_value); + if (config_iterator != config_item.labels.end()) { + SetDParam(idx++, STR_JUST_RAW_STRING); + SetDParamStr(idx++, config_iterator->second); + } else { + SetDParam(idx++, STR_JUST_INT); + SetDParam(idx++, current_value); + } } else { - SetDParam(idx++, STR_JUST_INT); - SetDParam(idx++, current_value); + int min_deviated = std::max(config_item.min_value, current_value - config_item.random_deviation); + auto config_iterator = config_item.labels.find(min_deviated); + if (config_iterator != config_item.labels.end()) { + SetDParam(idx++, STR_JUST_RAW_STRING); + SetDParamStr(idx++, config_iterator->second); + } else { + SetDParam(idx++, STR_JUST_INT); + SetDParam(idx++, min_deviated); + } + + int max_deviated = std::min(config_item.max_value, current_value + config_item.random_deviation); + config_iterator = config_item.labels.find(max_deviated); + if (config_iterator != config_item.labels.end()) { + SetDParam(idx++, STR_JUST_RAW_STRING); + SetDParamStr(idx++, config_iterator->second); + } else { + SetDParam(idx++, STR_JUST_INT); + SetDParam(idx++, max_deviated); + } } } diff --git a/src/script/script_info.cpp b/src/script/script_info.cpp index 22a4d7431a..10031519d6 100644 --- a/src/script/script_info.cpp +++ b/src/script/script_info.cpp @@ -162,12 +162,13 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm) } sq_pop(vm, 1); - /* Don't allow both random_deviation and SCRIPTCONFIG_RANDOM to + /* Don't allow both random_deviation and SCRIPTCONFIG_BOOLEAN to * be set for the same config item. */ - if ((items & 0x200) != 0 && (config.flags & SCRIPTCONFIG_RANDOM) != 0) { - this->engine->ThrowError("Setting both random_deviation and SCRIPTCONFIG_RANDOM is not allowed"); + if ((items & 0x200) != 0 && (config.flags & SCRIPTCONFIG_BOOLEAN) != 0) { + this->engine->ThrowError("setting both random_deviation and CONFIG_BOOLEAN is not allowed"); return SQ_ERROR; } + /* Reset the bit for random_deviation as it's optional. */ items &= ~0x200; diff --git a/src/settings.cpp b/src/settings.cpp index 5d43e7c62f..7ec3439623 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -219,8 +219,10 @@ enum IniFileVersion : uint32_t { IFV_GAME_TYPE, ///< 2 PR#9515 Convert server_advertise to server_game_type. IFV_LINKGRAPH_SECONDS, ///< 3 PR#10610 Store linkgraph update intervals in seconds instead of days. IFV_NETWORK_PRIVATE_SETTINGS, ///< 4 PR#10762 Move no_http_content_downloads / use_relay_service to private settings. + IFV_AUTOSAVE_RENAME, ///< 5 PR#11143 Renamed values of autosave to be in minutes. IFV_RIGHT_CLICK_CLOSE, ///< 6 PR#10204 Add alternative right click to close windows setting. + IFV_REMOVE_GENERATION_SEED, ///< 7 PR#11927 Remove "generation_seed" from configuration. IFV_MAX_VERSION, ///< Highest possible ini-file version. }; @@ -2988,6 +2990,13 @@ void SaveToConfig(SaveToConfigFlags flags) } } + if (generic_version < IFV_REMOVE_GENERATION_SEED) { + IniGroup *game_creation = generic_ini.GetGroup("game_creation"); + if (game_creation != nullptr) { + game_creation->RemoveItem("generation_seed"); + } + } + /* These variables are migrated from generic ini to private ini now. */ if (generic_version < IFV_NETWORK_PRIVATE_SETTINGS) { IniGroup *network = generic_ini.GetGroup("network"); diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 9eec09e988..421f1db65c 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2417,6 +2417,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("economy.inflation_fixed_dates")); accounting->Add(new SettingEntry("difficulty.initial_interest")); diff --git a/src/settings_type.h b/src/settings_type.h index 38172d67d1..b04c369261 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -116,6 +116,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 bool money_cheat_in_multiplayer; ///< is the money cheat permitted for non-admin multiplayer clients bool rename_towns_in_multiplayer; ///< is renaming towns permitted for non-admin multiplayer clients bool override_town_settings_in_multiplayer; ///< is overriding town settings permitted for non-admin multiplayer clients diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 91853f619e..fa102b9b05 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -529,7 +529,7 @@ static uint ShipAccelerate(Vehicle *v) { uint speed; - speed = std::min(v->cur_speed + 1, Ship::From(v)->GetEffectiveMaxSpeed()); + speed = std::min(v->cur_speed + v->acceleration, Ship::From(v)->GetEffectiveMaxSpeed()); speed = std::min(speed, v->current_order.GetMaxSpeed() * 2); if (v->breakdown_ctr == 1 && v->breakdown_type == BREAKDOWN_LOW_POWER && v->cur_speed > (v->breakdown_severity * ShipVehInfo(v->engine_type)->max_speed) >> 8) { @@ -972,6 +972,8 @@ static void ShipController(Ship *v) uint number_of_steps = ShipAccelerate(v); if (number_of_steps == 0 && v->current_order.IsType(OT_LEAVESTATION)) number_of_steps = 1; for (uint i = 0; i < number_of_steps; ++i) { + if (ShipMoveUpDownOnLock(v)) return; + GetNewVehiclePosResult gp = GetNewVehiclePos(v); if (v->state != TRACK_BIT_WORMHOLE) { /* Not on a bridge */ @@ -1202,6 +1204,7 @@ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, V v->sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = Random(); + v->acceleration = svi->acceleration; v->UpdateCache(); if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE); diff --git a/src/sl/extended_ver_sl.cpp b/src/sl/extended_ver_sl.cpp index c711d9bba9..5fdd73d213 100644 --- a/src/sl/extended_ver_sl.cpp +++ b/src/sl/extended_ver_sl.cpp @@ -208,6 +208,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_SAVEGAME_ID, XSCF_NULL, 1, 1, "slv_savegame_id", nullptr, nullptr, nullptr }, { XSLFI_NEWGRF_LAST_SERVICE, XSCF_NULL, 1, 1, "slv_newgrf_last_service", nullptr, nullptr, nullptr }, { XSLFI_CARGO_TRAVELLED, XSCF_NULL, 1, 1, "slv_cargo_travelled", nullptr, nullptr, nullptr }, + { XSLFI_SHIP_ACCELERATION, XSCF_NULL, 1, 1, "slv_ship_acceleration", nullptr, nullptr, nullptr }, { XSLFI_TABLE_PATS, XSCF_NULL, 1, 1, "table_pats", nullptr, nullptr, nullptr }, { XSLFI_TABLE_MISC_SL, XSCF_NULL, 1, 1, "table_misc_sl", nullptr, nullptr, nullptr }, diff --git a/src/sl/extended_ver_sl.h b/src/sl/extended_ver_sl.h index cf2deb5c3d..cbe6926248 100644 --- a/src/sl/extended_ver_sl.h +++ b/src/sl/extended_ver_sl.h @@ -157,6 +157,7 @@ enum SlXvFeatureIndex { XSLFI_SAVEGAME_ID, ///< See: SLV_SAVEGAME_ID (PR #10719) XSLFI_NEWGRF_LAST_SERVICE, ///< See: SLV_NEWGRF_LAST_SERVICE (PR #11124) XSLFI_CARGO_TRAVELLED, ///< See: SLV_CARGO_TRAVELLED (PR #11283) + XSLFI_SHIP_ACCELERATION, ///< See: SLV_SHIP_ACCELERATION (PR #10734) XSLFI_TABLE_PATS, ///< Use upstream table format for PATS XSLFI_TABLE_MISC_SL, ///< Use upstream table format for miscellaneous chunks, so far: DATE, VIEW, MAPS diff --git a/src/sl/saveload.cpp b/src/sl/saveload.cpp index 8443847b62..95e6a8fa5a 100644 --- a/src/sl/saveload.cpp +++ b/src/sl/saveload.cpp @@ -46,6 +46,7 @@ #include "../error.h" #include "../scope.h" #include "../core/ring_buffer.hpp" +#include "../timer/timer_game_tick.h" #include #include #ifdef __EMSCRIPTEN__ @@ -4115,14 +4116,23 @@ std::string GenerateDefaultSaveName() SetDParam(0, cid); - /* Insert current date */ - switch (_settings_client.gui.date_format_in_default_names) { - case 0: SetDParam(1, STR_JUST_DATE_LONG); break; - case 1: SetDParam(1, STR_JUST_DATE_TINY); break; - case 2: SetDParam(1, STR_JUST_DATE_ISO); break; - default: NOT_REACHED(); + /* We show the current game time differently depending on the timekeeping units used by this game. */ + if (EconTime::UsingWallclockUnits() && CalTime::IsCalendarFrozen()) { + /* Insert time played. */ + const auto play_time = _scaled_tick_counter / TICKS_PER_SECOND; + SetDParam(1, STR_SAVEGAME_DURATION_REALTIME); + SetDParam(2, play_time / 60 / 60); + SetDParam(3, (play_time / 60) % 60); + } else { + /* Insert current date */ + switch (_settings_client.gui.date_format_in_default_names) { + case 0: SetDParam(1, STR_JUST_DATE_LONG); break; + case 1: SetDParam(1, STR_JUST_DATE_TINY); break; + case 2: SetDParam(1, STR_JUST_DATE_ISO); break; + default: NOT_REACHED(); + } + SetDParam(2, CalTime::CurDate()); } - SetDParam(2, CalTime::CurDate()); /* Get the correct string (special string for when there's not company) */ std::string filename = GetString(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT); diff --git a/src/sl/saveload_common.h b/src/sl/saveload_common.h index 6d5f640a67..c355b0768c 100644 --- a/src/sl/saveload_common.h +++ b/src/sl/saveload_common.h @@ -383,6 +383,8 @@ enum SaveLoadVersion : uint16_t { SLV_ECONOMY_DATE, ///< 326 PR#10700 Split calendar and economy timers and dates. SLV_ECONOMY_MODE_TIMEKEEPING_UNITS, ///< 327 PR#11341 Mode to display economy measurements in wallclock units. 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/statusbar_gui.cpp b/src/statusbar_gui.cpp index 5abfb3d68f..85bd3eb351 100644 --- a/src/statusbar_gui.cpp +++ b/src/statusbar_gui.cpp @@ -162,6 +162,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/stdafx.h b/src/stdafx.h index 10dddb267a..e4d373e1cf 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -450,11 +450,11 @@ const char *assert_tile_info(uint32_t tile); /* Asserts are enabled if NDEBUG isn't defined or WITH_ASSERT is defined. */ #if !defined(NDEBUG) || defined(WITH_ASSERT) # undef assert -# define assert(expression) if (unlikely(!(expression))) error("Assertion failed at line %i of %s: %s", __LINE__, __FILE__, #expression); -# define assert_msg(expression, ...) if (unlikely(!(expression))) assert_msg_error(__LINE__, __FILE__, #expression, nullptr, __VA_ARGS__); -# define assert_msg_tile(expression, tile, ...) if (unlikely(!(expression))) assert_msg_error(__LINE__, __FILE__, #expression, assert_tile_info(tile), __VA_ARGS__); -# define assert_tile(expression, tile) if (unlikely(!(expression))) error("Assertion failed at line %i of %s: %s\n\t%s", __LINE__, __FILE__, #expression, assert_tile_info(tile)); -# define assert_str(expression, str) if (unlikely(!(expression))) assert_str_error(__LINE__, __FILE__, #expression, str); +# define assert(expression) do { if (unlikely(!(expression))) error("Assertion failed at line %i of %s: %s", __LINE__, __FILE__, #expression); } while (false) +# define assert_msg(expression, ...) do { if (unlikely(!(expression))) assert_msg_error(__LINE__, __FILE__, #expression, nullptr, __VA_ARGS__); } while (false) +# define assert_msg_tile(expression, tile, ...) do { if (unlikely(!(expression))) assert_msg_error(__LINE__, __FILE__, #expression, assert_tile_info(tile), __VA_ARGS__); } while (false) +# define assert_tile(expression, tile) do { if (unlikely(!(expression))) error("Assertion failed at line %i of %s: %s\n\t%s", __LINE__, __FILE__, #expression, assert_tile_info(tile)); } while (false) +# define assert_str(expression, str) do { if (unlikely(!(expression))) assert_str_error(__LINE__, __FILE__, #expression, str); } while (false) #else # undef assert # define assert(expression) diff --git a/src/strings.cpp b/src/strings.cpp index 9f0b23e58f..d317282eb5 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -522,7 +522,6 @@ static void FormatGenericCurrency(StringBuilder builder, const CurrencySpec *spe /* We are going to make number absolute for printing, so * keep this piece of data as we need it later on */ bool negative = number < 0; - const char *multiplier = ""; number *= spec->rate; @@ -539,16 +538,24 @@ static void FormatGenericCurrency(StringBuilder builder, const CurrencySpec *spe * The only remaining value is 1 (suffix), so everything that is not 1 */ if (spec->symbol_pos != 1) builder += spec->prefix; - /* for huge numbers, compact the number into k or M */ + StringID number_str = STR_NULL; + + /* For huge numbers, compact the number. */ if (compact) { - /* Take care of the 'k' rounding. Having 1 000 000 k + /* Take care of the thousand rounding. Having 1 000 000 k * and 1 000 M is inconsistent, so always use 1 000 M. */ - if (number >= 1000000000 - 500) { - number = (number + 500000) / 1000000; - multiplier = NBSP "M"; - } else if (number >= 1000000) { - number = (number + 500) / 1000; - multiplier = NBSP "k"; + if (number >= Money(1'000'000'000'000'000) - 500'000'000) { + number = (number + Money(500'000'000'000)) / Money(1'000'000'000'000); + number_str = STR_CURRENCY_SHORT_TERA; + } else if (number >= Money(1'000'000'000'000) - 500'000) { + number = (number + 500'000'000) / 1'000'000'000; + number_str = STR_CURRENCY_SHORT_GIGA; + } else if (number >= 1'000'000'000 - 500) { + number = (number + 500'000) / 1'000'000; + number_str = STR_CURRENCY_SHORT_MEGA; + } else if (number >= 1'000'000) { + number = (number + 500) / 1'000; + number_str = STR_CURRENCY_SHORT_KILO; } } @@ -556,7 +563,10 @@ static void FormatGenericCurrency(StringBuilder builder, const CurrencySpec *spe if (StrEmpty(separator)) separator = _currency->separator.c_str(); if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency; FormatNumber(builder, number, separator); - builder += multiplier; + if (number_str != STR_NULL) { + auto tmp_params = ArrayStringParameters<0>(); + FormatString(builder, GetStringPtr(number_str), tmp_params); + } /* Add suffix part, following symbol_pos specification. * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix). diff --git a/src/table/engines.h b/src/table/engines.h index 25b599df8f..d385a439cd 100644 --- a/src/table/engines.h +++ b/src/table/engines.h @@ -557,29 +557,30 @@ static const RailVehicleInfo _orig_rail_vehicle_info[] = { * @see ShipVehicleInfo * @param a image_index * @param b cost_factor - * @param c max_speed (1 unit = 1/3.2 mph = 0.5 km-ish/h) - * @param d capacity (persons, bags, tons, pieces, items, cubic metres, ...) - * @param e running_cost - * @param f sound effect - * @param g refittable + * @param c acceleration (1 unit = 1/3.2 mph per tick = 0.5 km-ish/h per tick) + * @param d max_speed (1 unit = 1/3.2 mph = 0.5 km-ish/h) + * @param e capacity (persons, bags, tons, pieces, items, cubic metres, ...) + * @param f running_cost + * @param g sound effect + * @param h refittable */ -#define SVI(a, b, c, d, e, f, g) { a, b, c, d, e, f, g, VE_DEFAULT, 0, 0 } +#define SVI(a, b, c, d, e, f, g, h) { a, b, c, d, e, f, g, h, VE_DEFAULT, 0, 0 } static const ShipVehicleInfo _orig_ship_vehicle_info[] = { - /* image_index capacity refittable - * | cost_factor running_cost | - * | | max_speed | sfx | - * | | | | | | | */ - SVI( 1, 160, 48, 220, 140, SND_06_DEPARTURE_CARGO_SHIP, 0 ), // 0 MPS Oil Tanker - SVI( 1, 176, 80, 350, 125, SND_06_DEPARTURE_CARGO_SHIP, 0 ), // 1 CS-Inc. Oil Tanker - SVI( 2, 96, 64, 100, 90, SND_07_DEPARTURE_FERRY, 0 ), // 2 MPS Passenger Ferry - SVI( 2, 112, 128, 130, 80, SND_07_DEPARTURE_FERRY, 0 ), // 3 FFP Passenger Ferry - SVI( 3, 148, 224, 100, 190, SND_07_DEPARTURE_FERRY, 0 ), // 4 Bakewell 300 Hovercraft - SVI( 2, 96, 64, 100, 90, SND_07_DEPARTURE_FERRY, 0 ), // 5 Chugger-Chug Passenger Ferry - SVI( 2, 112, 128, 130, 80, SND_07_DEPARTURE_FERRY, 0 ), // 6 Shivershake Passenger Ferry - SVI( 0, 128, 48, 160, 150, SND_06_DEPARTURE_CARGO_SHIP, 1 ), // 7 Yate Cargo ship - SVI( 0, 144, 80, 190, 113, SND_06_DEPARTURE_CARGO_SHIP, 1 ), // 8 Bakewell Cargo ship - SVI( 0, 128, 48, 160, 150, SND_06_DEPARTURE_CARGO_SHIP, 1 ), // 9 Mightymover Cargo ship - SVI( 0, 144, 80, 190, 113, SND_06_DEPARTURE_CARGO_SHIP, 1 ), // 10 Powernaut Cargo ship + /* image_index max_speed sfx refittable + * | cost_factor capacity | | + * | | acceleration running_cost | + * | | | | | | | | */ + SVI( 1, 160, 1, 48, 220, 140, SND_06_DEPARTURE_CARGO_SHIP, 0 ), // 0 MPS Oil Tanker + SVI( 1, 176, 1, 80, 350, 125, SND_06_DEPARTURE_CARGO_SHIP, 0 ), // 1 CS-Inc. Oil Tanker + SVI( 2, 96, 1, 64, 100, 90, SND_07_DEPARTURE_FERRY, 0 ), // 2 MPS Passenger Ferry + SVI( 2, 112, 1, 128, 130, 80, SND_07_DEPARTURE_FERRY, 0 ), // 3 FFP Passenger Ferry + SVI( 3, 148, 1, 224, 100, 190, SND_07_DEPARTURE_FERRY, 0 ), // 4 Bakewell 300 Hovercraft + SVI( 2, 96, 1, 64, 100, 90, SND_07_DEPARTURE_FERRY, 0 ), // 5 Chugger-Chug Passenger Ferry + SVI( 2, 112, 1, 128, 130, 80, SND_07_DEPARTURE_FERRY, 0 ), // 6 Shivershake Passenger Ferry + SVI( 0, 128, 1, 48, 160, 150, SND_06_DEPARTURE_CARGO_SHIP, 1 ), // 7 Yate Cargo ship + SVI( 0, 144, 1, 80, 190, 113, SND_06_DEPARTURE_CARGO_SHIP, 1 ), // 8 Bakewell Cargo ship + SVI( 0, 128, 1, 48, 160, 150, SND_06_DEPARTURE_CARGO_SHIP, 1 ), // 9 Mightymover Cargo ship + SVI( 0, 144, 1, 80, 190, 113, SND_06_DEPARTURE_CARGO_SHIP, 1 ), // 10 Powernaut Cargo ship }; #undef SVI diff --git a/src/table/settings/difficulty_settings.ini b/src/table/settings/difficulty_settings.ini index f474db1886..3550c91256 100644 --- a/src/table/settings/difficulty_settings.ini +++ b/src/table/settings/difficulty_settings.ini @@ -135,7 +135,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 @@ -371,3 +371,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/table/settings/world_settings.ini b/src/table/settings/world_settings.ini index 1aa4fe4e6b..ad1ac06488 100644 --- a/src/table/settings/world_settings.ini +++ b/src/table/settings/world_settings.ini @@ -238,6 +238,7 @@ strval = STR_VARIETY_NONE var = game_creation.generation_seed type = SLE_UINT32 from = SLV_30 +flags = SF_NOT_IN_CONFIG def = GENERATE_NEW_SEED min = 0 max = UINT32_MAX diff --git a/src/tests/bitmath_func.cpp b/src/tests/bitmath_func.cpp index 7b2fb9c21c..9b23684174 100644 --- a/src/tests/bitmath_func.cpp +++ b/src/tests/bitmath_func.cpp @@ -5,7 +5,7 @@ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ -/** @file bitmath_func_test.cpp Test functionality from core/bitmath_func. */ +/** @file bitmath_func.cpp Test functionality from core/bitmath_func. */ #include "../stdafx.h" diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 4062c85296..081aaa6826 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -19,6 +19,7 @@ #include "viewport_kdtree.h" #include "cmd_helper.h" #include "command_func.h" +#include "company_func.h" #include "industry.h" #include "station_base.h" #include "waypoint_base.h" @@ -1546,7 +1547,7 @@ static inline bool RoadTypesAllowHouseHere(TileIndex t) TileIndex cur_tile = t + ToTileIndexDiff(*ptr); if (!IsValidTile(cur_tile)) continue; - if (!(IsTileType(cur_tile, MP_ROAD) || IsTileType(cur_tile, MP_STATION))) continue; + if (!(IsTileType(cur_tile, MP_ROAD) || IsAnyRoadStopTile(cur_tile))) continue; allow = true; RoadType road_rt = GetRoadTypeRoad(cur_tile); @@ -3863,7 +3864,7 @@ uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t) if (cid != COMPANY_SPECTATOR && !(_settings_game.economy.bribe && t->unwanted[cid])) { /* Things worth more than this are not shown */ - Money avail = Company::Get(cid)->money + _price[PR_STATION_VALUE] * 200; + Money avail = GetAvailableMoney(cid) + _price[PR_STATION_VALUE] * 200; /* Check the action bits for validity and * if they are valid add them */ diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 5089aafaa2..604565cca5 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -277,7 +277,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; @@ -331,7 +331,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;