diff --git a/src/command.cpp b/src/command.cpp index 7267c0ef76..a9d3c27760 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -121,6 +121,7 @@ CommandProc CmdBuildIndustry; CommandProc CmdIndustrySetFlags; CommandProc CmdIndustrySetExclusivity; CommandProc CmdIndustrySetText; +CommandProc CmdIndustrySetProduction; CommandProc CmdSetCompanyManagerFace; CommandProc CmdSetCompanyColour; @@ -385,6 +386,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdIndustrySetFlags, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_INDUSTRY_SET_FLAGS DEF_CMD(CmdIndustrySetExclusivity, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_INDUSTRY_SET_EXCLUSIVITY DEF_CMD(CmdIndustrySetText, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_INDUSTRY_SET_TEXT + DEF_CMD(CmdIndustrySetProduction, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_INDUSTRY_SET_PRODUCTION DEF_CMD(CmdSetCompanyManagerFace, 0, CMDT_OTHER_MANAGEMENT ), // CMD_SET_COMPANY_MANAGER_FACE DEF_CMD(CmdSetCompanyColour, 0, CMDT_OTHER_MANAGEMENT ), // CMD_SET_COMPANY_COLOUR diff --git a/src/command_type.h b/src/command_type.h index 8405500748..f6c28b0f38 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -351,6 +351,7 @@ enum Commands { CMD_INDUSTRY_SET_FLAGS, ///< change industry control flags CMD_INDUSTRY_SET_EXCLUSIVITY, ///< change industry exclusive consumer/supplier CMD_INDUSTRY_SET_TEXT, ///< change additional text for the industry + CMD_INDUSTRY_SET_PRODUCTION, ///< change industry production CMD_SET_COMPANY_MANAGER_FACE, ///< set the manager's face of the company CMD_SET_COMPANY_COLOUR, ///< set the colour of the company diff --git a/src/industry.h b/src/industry.h index 61e3866b12..ea3ca4c310 100644 --- a/src/industry.h +++ b/src/industry.h @@ -50,8 +50,10 @@ enum IndustryControlFlags : byte { * Industry can not close regardless of production level or time since last delivery. * This does not prevent a closure already announced. */ INDCTL_NO_CLOSURE = 1 << 2, + /** Indicates that the production level of the industry is externally controlled. */ + INDCTL_EXTERNAL_PROD_LEVEL = 1 << 3, /** Mask of all flags set */ - INDCTL_MASK = INDCTL_NO_PRODUCTION_DECREASE | INDCTL_NO_PRODUCTION_INCREASE | INDCTL_NO_CLOSURE, + INDCTL_MASK = INDCTL_NO_PRODUCTION_DECREASE | INDCTL_NO_PRODUCTION_INCREASE | INDCTL_NO_CLOSURE | INDCTL_EXTERNAL_PROD_LEVEL, }; DECLARE_ENUM_AS_BIT_SET(IndustryControlFlags); diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 371fc211dd..ea1693feee 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -66,6 +66,8 @@ IndustrySpec _industry_specs[NUM_INDUSTRYTYPES]; IndustryTileSpec _industry_tile_specs[NUM_INDUSTRYTILES]; IndustryBuildData _industry_builder; ///< In-game manager of industries. +static int WhoCanServiceIndustry(Industry *ind); + /** * This function initialize the spec arrays of both * industry and industry tiles. @@ -2204,6 +2206,75 @@ CommandCost CmdIndustrySetFlags(TileIndex tile, DoCommandFlag flags, uint32 p1, return CommandCost(); } +/** + * Set industry production. + * @param flags Type of operation. + * @param ind_id IndustryID + * @param prod_level Production level. + * @param show_news Show a news message on production change. + * @return Empty cost or an error. + */ +CommandCost CmdIndustrySetProduction(DoCommandFlag flags, IndustryID ind_id, byte prod_level, bool show_news) +{ + if (_current_company != OWNER_DEITY) return CMD_ERROR; + if (prod_level < PRODLEVEL_MINIMUM || prod_level > PRODLEVEL_MAXIMUM) return CMD_ERROR; + + Industry *ind = Industry::GetIfValid(ind_id); + if (ind == nullptr) return CMD_ERROR; + + if (flags & DC_EXEC) { + StringID str = STR_NULL; + if (prod_level > ind->prod_level) { + str = GetIndustrySpec(ind->type)->production_up_text; + } else if (prod_level < ind->prod_level) { + str = GetIndustrySpec(ind->type)->production_down_text; + } + + ind->ctlflags |= INDCTL_EXTERNAL_PROD_LEVEL; + ind->prod_level = prod_level; + ind->RecomputeProductionMultipliers(); + + /* Show news message if requested. */ + if (show_news && str != STR_NULL) { + NewsType nt; + switch (WhoCanServiceIndustry(ind)) { + case 0: nt = NT_INDUSTRY_NOBODY; break; + case 1: nt = NT_INDUSTRY_OTHER; break; + case 2: nt = NT_INDUSTRY_COMPANY; break; + default: NOT_REACHED(); + } + + /* Set parameters of news string */ + if (str > STR_LAST_STRINGID) { + SetDParam(0, STR_TOWN_NAME); + SetDParam(1, ind->town->index); + SetDParam(2, GetIndustrySpec(ind->type)->name); + } else { + SetDParam(0, ind->index); + } + AddIndustryNewsItem(str, nt, ind->index); + } + } + + return CommandCost(); +} + +/** + * Set industry production. + * @param tile unused. + * @param flags type of operation + * @param p1 IndustryID. + * @param p2 various bitstuffed elements + * - p2 = (bit 0 - 7) - production level + * - p2 = (bit 8) - whether to show news + * @param text unused. + * @return the cost of this operation or an error + */ +CommandCost CmdIndustrySetProduction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + return CmdIndustrySetProduction(flags, (IndustryID)p1, GB(p2, 0, 8), HasBit(p2, 8)); +} + /** * Change exclusive consumer or supplier for the industry. * @param flags Type of operation. @@ -2717,7 +2788,7 @@ static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accept * service the industry, and 1 otherwise (only competitors can service the * industry) */ -static int WhoCanServiceIndustry(Industry *ind) +int WhoCanServiceIndustry(Industry *ind) { if (ind->stations_near.size() == 0) return 0; // No stations found at all => nobody services @@ -2919,6 +2990,11 @@ static void ChangeIndustryProduction(Industry *i, bool monthly) /* If override flags are set, prevent actually changing production if any was decided on */ if ((i->ctlflags & INDCTL_NO_PRODUCTION_DECREASE) && (div > 0 || increment < 0)) return; if ((i->ctlflags & INDCTL_NO_PRODUCTION_INCREASE) && (mul > 0 || increment > 0)) return; + if (i->ctlflags & INDCTL_EXTERNAL_PROD_LEVEL) { + div = 0; + mul = 0; + increment = 0; + } if (!callback_enabled && (indspec->life_type & INDUSTRYLIFE_PROCESSING)) { if ((_cur_year - i->last_prod_year) >= PROCESSING_INDUSTRY_ABANDONMENT_YEARS && Chance16(1, original_economy ? 2 : 180)) { diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index fdd9d4ec8c..d6267c7aab 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -78,6 +78,8 @@ * \li GSVehicleList_DefaultGroup * \li GSGoal::IsValidGoalDestination * \li GSGoal::SetDestination + * \li GSIndustry::GetProductionLevel + * \li GSIndustry::SetProductionLevel * * API removals: * \li GSError::ERR_PRECONDITION_TOO_MANY_PARAMETERS, that error is never returned anymore. diff --git a/src/script/api/script_industry.cpp b/src/script/api/script_industry.cpp index 74983309af..3d2274d28d 100644 --- a/src/script/api/script_industry.cpp +++ b/src/script/api/script_industry.cpp @@ -292,3 +292,19 @@ ::Owner owner = (company == ScriptCompany::COMPANY_INVALID ? ::INVALID_OWNER : (::Owner)company); return ScriptObject::DoCommand(0, industry_id, ((uint8)owner), CMD_INDUSTRY_SET_EXCLUSIVITY); } + +/* static */ SQInteger ScriptIndustry::GetProductionLevel(IndustryID industry_id) +{ + Industry *i = Industry::GetIfValid(industry_id); + if (i == nullptr) return 0; + return i->prod_level; +} + +/* static */ bool ScriptIndustry::SetProductionLevel(IndustryID industry_id, SQInteger prod_level, bool show_news) +{ + EnforceDeityMode(false); + EnforcePrecondition(false, IsValidIndustry(industry_id)); + EnforcePrecondition(false, prod_level >= PRODLEVEL_MINIMUM && prod_level <= PRODLEVEL_MAXIMUM); + + return ScriptObject::DoCommand(0, industry_id, ((uint8)prod_level) | (show_news ? (1 << 8) : 0), CMD_INDUSTRY_SET_PRODUCTION); +} diff --git a/src/script/api/script_industry.hpp b/src/script/api/script_industry.hpp index ac61dc10aa..3fcec5d8e6 100644 --- a/src/script/api/script_industry.hpp +++ b/src/script/api/script_industry.hpp @@ -48,6 +48,10 @@ public: * This does not prevent a closure already announced. */ INDCTL_NO_CLOSURE = ::INDCTL_NO_CLOSURE, + /** + * Indicates that the production level of the industry is controlled by a game script. + */ + INDCTL_EXTERNAL_PROD_LEVEL = ::INDCTL_EXTERNAL_PROD_LEVEL, }; /** @@ -324,6 +328,27 @@ public: */ static bool SetExclusiveConsumer(IndustryID industry_id, ScriptCompany::CompanyID company_id); + /** + * Gets the current production level of an industry. + * @param industry_id The index of the industry. + * @api -ai + */ + static SQInteger GetProductionLevel(IndustryID industry_id); + + /** + * Sets the current production level of an industry. + * @note Setting the production level automatically sets the control flag INDCTL_EXTERNAL_PROD_LEVEL if it wasn't already set. + * Normal production behaviour can be restored by clearing the control flag. + * @param industry_id The index of the industry. + * @param prod_level The production level to set. + * @param show_news If set to true and the production changed, generate a production change news message. If set to false, no news message is shown. + * @pre IsValidIndustry(industry_id). + * @pre ScriptCompanyMode::IsDeity(). + * @pre prod_level >= 4 && prod_level <= 128. + * @return True if the action succeeded. + * @api -ai + */ + static bool SetProductionLevel(IndustryID industry_id, SQInteger prod_level, bool show_news); }; #endif /* SCRIPT_INDUSTRY_HPP */