Skip to content

Commit

Permalink
Feature: Settings to scale cargo production of towns and industries
Browse files Browse the repository at this point in the history
  • Loading branch information
2TallTyler committed Jan 19, 2024
1 parent 9031191 commit aac9a7c
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 30 deletions.
46 changes: 46 additions & 0 deletions src/economy_func.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "cargo_type.h"
#include "vehicle_type.h"
#include "company_type.h"
#include "settings_type.h"
#include "core/random_func.hpp"

void ResetPriceBaseMultipliers();
void SetPriceBaseMultiplier(Price price, int factor);
Expand Down Expand Up @@ -49,4 +51,48 @@ inline bool EconomyIsInRecession()
return _economy.fluct <= 0;
}

/**
* Scale a number by the inverse of the cargo scale setting, e.g. a scale of 25% multiplies the number by 4.
* @param num The number to scale.
* @param town Are we scaling town production, or industry production?
* @return The number scaled by the inverse of the cargo scale setting, minimum of 1.
*/
static uint ScaleByInverseCargoScale(uint num, bool town)
{
uint16_t percentage = (town ? _settings_game.economy.town_cargo_scale : _settings_game.economy.industry_cargo_scale);

/* We might not need to do anything. */
if (percentage == 100) return num;

/* Never return 0, since we often divide by this number. */
return std::max((num * 100) / percentage, 1u);
}

/**
* Scale a number by the cargo scale setting.
* @param num The number to scale.
* @param town Are we scaling town production, or industry production?
* @return The number scaled by the current cargo scale setting. May be 0.
*/
inline uint ScaleByCargoScale(uint num, bool town)
{
/* Don't bother scaling in the menu, especially since settings don't exist when starting OpenTTD and trying to read them crashes the game. */
if (_game_mode == GM_MENU) return num;

if (num == 0) return num;

uint16_t percentage = (town ? _settings_game.economy.town_cargo_scale : _settings_game.economy.industry_cargo_scale);

/* We might not need to do anything. */
if (percentage == 100) return num;

uint scaled = (num * percentage) / 100;

/* We might round down to 0, so we compensate with a random chance approximately equal to the economy scale,
* e.g. at 25% scale there's a 1/4 chance to round up to 1. */
if (scaled == 0 && Chance16(1, ScaleByInverseCargoScale(1, town))) return 1;

return scaled;
}

#endif /* ECONOMY_FUNC_H */
38 changes: 30 additions & 8 deletions src/industry_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,7 +1131,24 @@ static void ChopLumberMillTrees(Industry *i)

TileIndex tile = i->location.tile;
if (CircularTileSearch(&tile, 40, SearchLumberMillTrees, nullptr)) { // 40x40 tiles to search.
i->produced[0].waiting = ClampTo<uint16_t>(i->produced[0].waiting + 45); // Found a tree, add according value to waiting cargo.
i->produced[0].waiting = ClampTo<uint16_t>(i->produced[0].waiting + ScaleByCargoScale(45, false)); // Found a tree, add according value to waiting cargo.
}
}

/**
* Helper for ProduceIndustryGoods that scales and produces cargos.
* @param i The industry
* @param scale Should we scale production of this cargo directly?
*/
static void ProduceIndustryGoodsHelper(Industry *i, bool scale)
{
for (auto &p : i->produced) {
if (!IsValidCargoID(p.cargo)) continue;

uint16_t amount = p.rate;
if (scale) amount = ScaleByCargoScale(amount, false);

p.waiting = ClampTo<uint16_t>(p.waiting + amount);
}
}

Expand All @@ -1154,15 +1171,20 @@ static void ProduceIndustryGoods(Industry *i)

i->counter--;

/* produce some cargo */
/* If using an industry callback, scale the callback interval by cargo scale percentage. */
if (HasBit(indsp->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) {
if (i->counter % ScaleByInverseCargoScale(Ticks::INDUSTRY_PRODUCE_TICKS, false) == 0) {
IndustryProductionCallback(i, 1);
ProduceIndustryGoodsHelper(i, false);
}
}

/* All other production and special effects happen every 256 ticks, and cargo production is just scaled by the cargo scale percentage. */
if ((i->counter % Ticks::INDUSTRY_PRODUCE_TICKS) == 0) {
if (HasBit(indsp->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) IndustryProductionCallback(i, 1);
/* Handle non-callback cargo production. */
if (!HasBit(indsp->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) ProduceIndustryGoodsHelper(i, true);

IndustryBehaviour indbehav = indsp->behaviour;
for (auto &p : i->produced) {
if (!IsValidCargoID(p.cargo)) continue;
p.waiting = ClampTo<uint16_t>(p.waiting + p.rate);
}

if ((indbehav & INDUSTRYBEH_PLANT_FIELDS) != 0) {
uint16_t cb_res = CALLBACK_FAILED;
Expand Down Expand Up @@ -1824,7 +1846,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
}

for (auto &p : i->produced) {
p.history[LAST_MONTH].production += p.rate * 8;
p.history[LAST_MONTH].production += ScaleByCargoScale(p.rate * 8, false);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/industry_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ static void UpdateIndustryProduction(Industry *i)

for (auto &p : i->produced) {
if (IsValidCargoID(p.cargo)) {
p.history[LAST_MONTH].production = 8 * p.rate;
p.history[LAST_MONTH].production = ScaleByCargoScale(8 * p.rate, false);
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/lang/english.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,12 @@ STR_CONFIG_SETTING_WARN_INCOME_LESS_HELPTEXT :When enabled, a
STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES :Vehicles never expire: {STRING2}
STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_HELPTEXT :When enabled, all vehicle models remain available forever after their introduction

STR_CONFIG_SETTING_TOWN_CARGO_SCALE :Scale town cargo production: {STRING2}
STR_CONFIG_SETTING_TOWN_CARGO_SCALE_HELPTEXT :Scale the cargo production of towns by this percentage.
STR_CONFIG_SETTING_INDUSTRY_CARGO_SCALE :Scale industry cargo production: {STRING2}
STR_CONFIG_SETTING_INDUSTRY_CARGO_SCALE_HELPTEXT :Scale the cargo production of industries by this percentage.
STR_CONFIG_SETTING_CARGO_SCALE_VALUE :{NUM}%

STR_CONFIG_SETTING_AUTORENEW_VEHICLE :Autorenew vehicle when it gets old: {STRING2}
STR_CONFIG_SETTING_AUTORENEW_VEHICLE_HELPTEXT :When enabled, a vehicle nearing its end of life gets automatically replaced when the renew conditions are fulfilled

Expand Down
12 changes: 12 additions & 0 deletions src/object_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,13 @@ static void TileLoop_Object(TileIndex tile)
/* Top town buildings generate 250, so the top HQ type makes 256. */
if (GB(r, 0, 8) < (256 / 4 / (6 - level))) {
uint amt = GB(r, 0, 8) / 8 / 4 + 1;

/* Production is halved during recessions. */
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;

/* Scale by cargo scale setting. */
amt = ScaleByCargoScale(amt, true);

MoveGoodsToStation(CT_PASSENGERS, amt, SourceType::Headquarters, GetTileOwner(tile), stations.GetStations());
}

Expand All @@ -694,7 +700,13 @@ static void TileLoop_Object(TileIndex tile)
* equations. */
if (GB(r, 8, 8) < (196 / 4 / (6 - level))) {
uint amt = GB(r, 8, 8) / 8 / 4 + 1;

/* Production is halved during recessions. */
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;

/* Scale by cargo scale setting. */
amt = ScaleByCargoScale(amt, true);

MoveGoodsToStation(CT_MAIL, amt, SourceType::Headquarters, GetTileOwner(tile), stations.GetStations());
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/settings_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2032,6 +2032,7 @@ static SettingsContainer &GetSettingsTree()

SettingsPage *towns = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TOWNS));
{
towns->Add(new SettingEntry("economy.town_cargo_scale"));
towns->Add(new SettingEntry("economy.town_growth_rate"));
towns->Add(new SettingEntry("economy.allow_town_roads"));
towns->Add(new SettingEntry("economy.allow_town_level_crossings"));
Expand All @@ -2044,6 +2045,7 @@ static SettingsContainer &GetSettingsTree()

SettingsPage *industries = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES));
{
industries->Add(new SettingEntry("economy.industry_cargo_scale"));
industries->Add(new SettingEntry("difficulty.industry_density"));
industries->Add(new SettingEntry("construction.raw_industry_construction"));
industries->Add(new SettingEntry("construction.industry_platform"));
Expand Down
2 changes: 2 additions & 0 deletions src/settings_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,8 @@ struct EconomySettings {
uint16_t town_noise_population[4]; ///< population to base decision on noise evaluation (@see town_council_tolerance)
bool allow_town_level_crossings; ///< towns are allowed to build level crossings
bool infrastructure_maintenance; ///< enable monthly maintenance fee for owner infrastructure
uint16_t town_cargo_scale; ///< scale cargo production of towns by this percentage.
uint16_t industry_cargo_scale; ///< scale cargo production of industries by this percentage.
};

struct LinkGraphSettings {
Expand Down
26 changes: 26 additions & 0 deletions src/table/settings/economy_settings.ini
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,29 @@ str = STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE
strhelp = STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT
post_cb = [](auto) { InvalidateWindowClassesData(WC_COMPANY_INFRASTRUCTURE); }
cat = SC_BASIC

[SDT_VAR]
var = economy.town_cargo_scale
type = SLE_UINT16
flags = SF_NO_NETWORK
def = 100
min = 15
max = 400
interval = 1
str = STR_CONFIG_SETTING_TOWN_CARGO_SCALE
strhelp = STR_CONFIG_SETTING_TOWN_CARGO_SCALE_HELPTEXT
strval = STR_CONFIG_SETTING_CARGO_SCALE_VALUE
cat = SC_BASIC

[SDT_VAR]
var = economy.industry_cargo_scale
type = SLE_UINT16
flags = SF_NO_NETWORK
def = 100
min = 15
max = 400
interval = 1
str = STR_CONFIG_SETTING_INDUSTRY_CARGO_SCALE
strhelp = STR_CONFIG_SETTING_INDUSTRY_CARGO_SCALE_HELPTEXT
strval = STR_CONFIG_SETTING_CARGO_SCALE_VALUE
cat = SC_BASIC
59 changes: 38 additions & 21 deletions src/town_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,36 @@ static void AdvanceHouseConstruction(TileIndex tile)
}

/**
* Generate cargo for a house, scaled by the current economy scale.
* @param t The current town.
* @param ct Type of cargo to generate, usually CT_PASSENGERS or CT_MAIL.
* @param amount The number of cargo units.
* @param stations Available stations for this house.
* @param affected_by_recession Is this cargo halved during recessions?
*/
static void TownGenerateCargo(Town *t, CargoID ct, uint amount, StationFinder &stations, bool affected_by_recession)
{
if (amount == 0) return;

/* All production is halved during a recession (except for NewGRF-supplied town cargo). */
if (affected_by_recession && EconomyIsInRecession()) {
amount = (amount + 1) >> 1;
}

/* Scale by cargo scale setting. */
amount = ScaleByCargoScale(amount, true);

/* Actually generate cargo and update town statistics. */
uint moved = MoveGoodsToStation(ct, amount, SourceType::Town, t->index, stations.GetStations());

const CargoSpec *cs = CargoSpec::Get(ct);
t->supplied[cs->Index()].new_max += amount;
t->supplied[cs->Index()].new_act += moved;
}

/**
* Tile callback function.
*
* Tile callback function. Periodic tick handler for the tiles of a town.
* @param tile been asked to do its stuff
*/
Expand Down Expand Up @@ -563,30 +593,21 @@ static void TileLoop_Town(TileIndex tile)
uint amt = GB(callback, 0, 8);
if (amt == 0) continue;

uint moved = MoveGoodsToStation(cargo, amt, SourceType::Town, t->index, stations.GetStations());

const CargoSpec *cs = CargoSpec::Get(cargo);
t->supplied[cs->Index()].new_max += amt;
t->supplied[cs->Index()].new_act += moved;
/* NewGRF-supplied town cargos are not affected by recessions. */
TownGenerateCargo(t, cargo, amt, stations, false);
}
} else {
switch (_settings_game.economy.town_cargogen_mode) {
case TCGM_ORIGINAL:
/* Original (quadratic) cargo generation algorithm */
if (GB(r, 0, 8) < hs->population) {
uint amt = GB(r, 0, 8) / 8 + 1;

if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_PASSENGERS].new_max += amt;
t->supplied[CT_PASSENGERS].new_act += MoveGoodsToStation(CT_PASSENGERS, amt, SourceType::Town, t->index, stations.GetStations());
TownGenerateCargo(t, CT_PASSENGERS, amt, stations, true);
}

if (GB(r, 8, 8) < hs->mail_generation) {
uint amt = GB(r, 8, 8) / 8 + 1;

if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_MAIL].new_max += amt;
t->supplied[CT_MAIL].new_act += MoveGoodsToStation(CT_MAIL, amt, SourceType::Town, t->index, stations.GetStations());
TownGenerateCargo(t, CT_MAIL, amt, stations, true);
}
break;

Expand All @@ -601,18 +622,14 @@ static void TileLoop_Town(TileIndex tile)
/* Mask random value by potential pax and count number of actual pax */
uint amt = CountBits(r & genmask);
/* Adjust and apply */
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_PASSENGERS].new_max += amt;
t->supplied[CT_PASSENGERS].new_act += MoveGoodsToStation(CT_PASSENGERS, amt, SourceType::Town, t->index, stations.GetStations());
TownGenerateCargo(t, CT_PASSENGERS, amt, stations, true);

/* Do the same for mail, with a fresh random */
r = Random();
genmax = (hs->mail_generation + 7) / 8;
genmask = (genmax >= 32) ? 0xFFFFFFFF : ((1 << genmax) - 1);
amt = CountBits(r & genmask);
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_MAIL].new_max += amt;
t->supplied[CT_MAIL].new_act += MoveGoodsToStation(CT_MAIL, amt, SourceType::Town, t->index, stations.GetStations());
TownGenerateCargo(t, CT_MAIL, amt, stations, true);
}
break;

Expand Down Expand Up @@ -1866,8 +1883,8 @@ void UpdateTownRadius(Town *t)
*/
void UpdateTownMaxPass(Town *t)
{
t->supplied[CT_PASSENGERS].old_max = t->cache.population >> 3;
t->supplied[CT_MAIL].old_max = t->cache.population >> 4;
t->supplied[CT_PASSENGERS].old_max = ScaleByCargoScale(t->cache.population >> 3, true);
t->supplied[CT_MAIL].old_max = ScaleByCargoScale(t->cache.population >> 4, true);
}

static void UpdateTownGrowthRate(Town *t);
Expand Down

0 comments on commit aac9a7c

Please sign in to comment.