Add: Cargo delivery distribution modes

pull/352/head
Yourself 2 years ago committed by Jonathan G Rennison
parent 2c3b502441
commit e4fa9f8465

@ -1075,30 +1075,9 @@ Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, C
/** The industries we've currently brought cargo to. */
static SmallIndustryList _cargo_delivery_destinations;
/**
* Transfer goods from station to industry.
* All cargo is delivered to the nearest (Manhattan) industry to the station sign, which is inside the acceptance rectangle and actually accepts the cargo.
* @param st The station that accepted the cargo
* @param cargo_type Type of cargo delivered
* @param num_pieces Amount of cargo delivered
* @param source The source of the cargo
* @param company The company delivering the cargo
* @return actually accepted pieces of cargo
*/
static uint DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, uint num_pieces, IndustryID source, CompanyID company)
{
/* Find the nearest industrytile to the station sign inside the catchment area, whose industry accepts the cargo.
* This fails in three cases:
* 1) The station accepts the cargo because there are enough houses around it accepting the cargo.
* 2) The industries in the catchment area temporarily reject the cargo, and the daily station loop has not yet updated station acceptance.
* 3) The results of callbacks CBID_INDUSTRY_REFUSE_CARGO and CBID_INDTILE_CARGO_ACCEPTANCE are inconsistent. (documented behaviour)
*/
uint accepted = 0;
template <class F>
void ForAcceptingIndustries(const Station *st, CargoID cargo_type, IndustryID source, CompanyID company, F&& f) {
for (Industry *ind : st->industries_near) {
if (num_pieces == 0) break;
if (ind->index == source) continue;
uint cargo_index;
@ -1113,6 +1092,22 @@ static uint DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, uint n
if (ind->exclusive_supplier != INVALID_OWNER && ind->exclusive_supplier != st->owner) continue;
if (!f(ind, cargo_index)) break;
}
}
uint DeliverGoodsToIndustryNearestFirst(const Station *st, CargoID cargo_type, uint num_pieces, IndustryID source, CompanyID company)
{
/* Find the nearest industrytile to the station sign inside the catchment area, whose industry accepts the cargo.
* This fails in three cases:
* 1) The station accepts the cargo because there are enough houses around it accepting the cargo.
* 2) The industries in the catchment area temporarily reject the cargo, and the daily station loop has not yet updated station acceptance.
* 3) The results of callbacks CBID_INDUSTRY_REFUSE_CARGO and CBID_INDTILE_CARGO_ACCEPTANCE are inconsistent. (documented behaviour)
*/
uint accepted = 0;
ForAcceptingIndustries(st, cargo_type, source, company, [&](Industry *ind, uint cargo_index) {
/* Insert the industry into _cargo_delivery_destinations, if not yet contained */
include(_cargo_delivery_destinations, ind);
@ -1124,11 +1119,116 @@ static uint DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, uint n
/* Update the cargo monitor. */
AddCargoDelivery(cargo_type, company, amount, ST_INDUSTRY, source, st, ind->index);
return num_pieces != 0;
});
return accepted;
}
uint DeliverGoodsToIndustryEqually(const Station *st, CargoID cargo_type, uint num_pieces, IndustryID source, CompanyID company)
{
struct AcceptingIndustry {
Industry *ind;
uint cargo_index;
uint capacity;
uint delivered;
};
std::vector<AcceptingIndustry> acceptingIndustries;
uint min_capacity = 0xFFFFu;
ForAcceptingIndustries(st, cargo_type, source, company, [&](Industry *ind, uint cargo_index) {
uint capacity = 0xFFFFu - ind->incoming_cargo_waiting[cargo_index];
min_capacity = std::min(min_capacity, capacity);
acceptingIndustries.push_back({ind, cargo_index, capacity, 0});
return true;
});
uint accepted = 0;
auto distributeCargo = [&](AcceptingIndustry &e, uint amount) {
e.ind->incoming_cargo_waiting[e.cargo_index] += amount;
e.ind->last_cargo_accepted_at[e.cargo_index] = _date;
e.capacity -= amount;
e.delivered += amount;
num_pieces -= amount;
accepted += amount;
};
auto finalizeCargo = [&](AcceptingIndustry &e) {
include(_cargo_delivery_destinations, e.ind);
AddCargoDelivery(cargo_type, company, e.delivered, ST_INDUSTRY, source, st, e.ind->index);
};
// Handle low-capacity industries first
while (!acceptingIndustries.empty() && min_capacity * acceptingIndustries.size() <= num_pieces) {
uint amount = min_capacity;
min_capacity = 0xFFFFu;
for (auto &e : acceptingIndustries) {
distributeCargo(e, amount);
if (e.capacity > 0) {
min_capacity = std::min(min_capacity, e.capacity);
} else {
finalizeCargo(e);
}
}
auto removeIt = std::remove_if(acceptingIndustries.begin(), acceptingIndustries.end(), [](const auto &e) { return e.capacity == 0; });
acceptingIndustries.erase(removeIt, acceptingIndustries.end());
}
// Remaining industries can accept all remaining cargo when distributed evenly
if (!acceptingIndustries.empty()) {
uint amount = num_pieces / static_cast<uint>(acceptingIndustries.size());
if (amount > 0) {
for (auto &e : acceptingIndustries) {
distributeCargo(e, amount);
}
}
// If cargo didn't divide evenly into remaining industries, distribute the remainder randomly
if (num_pieces > 0) {
assert(num_pieces < acceptingIndustries.size());
for (uint32 i = 0; i < num_pieces; ++i) {
uint32 j = i + _random.Next(static_cast<uint32>(acceptingIndustries.size()) - i);
std::swap(acceptingIndustries[i], acceptingIndustries[j]);
distributeCargo(acceptingIndustries[i], 1);
}
assert(num_pieces == 0);
}
for (auto &e : acceptingIndustries) {
finalizeCargo(e);
}
}
return accepted;
}
/**
* Transfer goods from station to industry.
* All cargo is delivered to the nearest (Manhattan) industry to the station sign, which is inside the acceptance rectangle and actually accepts the cargo.
* @param st The station that accepted the cargo
* @param cargo_type Type of cargo delivered
* @param num_pieces Amount of cargo delivered
* @param source The source of the cargo
* @param company The company delivering the cargo
* @return actually accepted pieces of cargo
*/
static uint DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, uint num_pieces, IndustryID source, CompanyID company)
{
switch(_settings_game.station.station_delivery_mode) {
case SD_BALANCED:
return DeliverGoodsToIndustryEqually(st, cargo_type, num_pieces, source, company);
default:
return DeliverGoodsToIndustryNearestFirst(st, cargo_type, num_pieces, source, company);
}
}
/**
* Delivers goods to industries/towns and calculates the payment
* @param num_pieces amount of cargo delivered

@ -1259,6 +1259,8 @@ STR_CONFIG_SETTING_TRAIN_BRAKING_REALISTIC :Realistic {PUSH
STR_CONFIG_SETTING_REALISTIC_BRAKING_SIGNALS_NOT_ALLOWED :{WHITE}Realistic braking can't be enabled. At least one pre-signal or two-way signal is present.
STR_CONFIG_SETTING_DELIVERY_BALANCED :Balanced
###length 3
STR_CONFIG_SETTING_HORIZONTAL_POS_LEFT :Left
STR_CONFIG_SETTING_HORIZONTAL_POS_CENTER :Centre
@ -1340,6 +1342,9 @@ STR_CONFIG_SETTING_STATION_RATING_SIZE_CARGO_AMOUNT_HELPTEXT :When enabled, t
STR_CONFIG_SETTING_EXTRADYNAMITE :Allow removal of more town-owned roads, bridges and tunnels: {STRING2}
STR_CONFIG_SETTING_EXTRADYNAMITE_HELPTEXT :Make it easier to remove town-owned infrastructure and buildings
STR_CONFIG_SETTING_CARGO_DELIVERY_MODE :Cargo delivery distribution mode: {STRING2}
STR_CONFIG_SETTING_CARGO_DELIVERY_MODE_HELPTEXT :The method used to deliver cargo to industries surrounding a station.
STR_CONFIG_SETTING_TRAIN_LENGTH :Maximum length of trains: {STRING2}
STR_CONFIG_SETTING_TRAIN_LENGTH_HELPTEXT :Set the maximum length of trains
STR_CONFIG_SETTING_TILE_LENGTH :{COMMA} tile{P 0 "" s}

@ -2181,6 +2181,7 @@ static SettingsContainer &GetSettingsTree()
industries->Add(new SettingEntry("economy.type"));
industries->Add(new SettingEntry("station.serve_neutral_industries"));
industries->Add(new SettingEntry("economy.industry_cargo_scale_factor"));
industries->Add(new SettingEntry("station.station_delivery_mode"));
}
SettingsPage *cdist = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST));

@ -21,6 +21,7 @@
#include "zoom_type.h"
#include "openttd.h"
#include "rail_gui_type.h"
#include "station_type.h"
/* Used to validate sizes of "max" value in settings. */
const size_t MAX_SLE_UINT8 = UINT8_MAX;
@ -715,6 +716,7 @@ struct StationSettings {
byte catchment_increase; ///< amount by which station catchment is increased
bool cargo_class_rating_wait_time; ///< station rating tolerance to time since last cargo pickup depends on cargo class
bool station_size_rating_cargo_amount; ///< station rating tolerance to waiting cargo amount depends on station size
StationDelivery station_delivery_mode; ///< method to use for distributing cargo from stations to accepting industries
};
/** Default settings for vehicles. */

@ -87,6 +87,11 @@ enum CatchmentArea {
MAX_CATCHMENT = 10, ///< Maximum catchment for airports with "modified catchment" enabled
};
enum StationDelivery : byte {
SD_NEAREST_FIRST = 0, ///< Station delivers cargo only to the nearest accepting industry
SD_BALANCED = 1 ///< Station delivers cargo equally among accepting industries
};
static const uint MAX_LENGTH_STATION_NAME_CHARS = 128; ///< The maximum length of a station name in characters including '\0'
struct StationCompare {

@ -122,6 +122,12 @@ static const SettingDescEnumEntry _train_braking_model[] = {
{ 0, STR_NULL }
};
static const SettingDescEnumEntry _station_delivery_mode[] = {
{ SD_NEAREST_FIRST, STR_CONFIG_SETTING_ORIGINAL},
{ SD_BALANCED, STR_CONFIG_SETTING_DELIVERY_BALANCED},
{ 0, STR_NULL }
};
/* Some settings do not need to be synchronised when playing in multiplayer.
* These include for example the GUI settings and will not be saved with the
* savegame.
@ -1948,6 +1954,14 @@ str = STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES
strhelp = STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES_HELPTEXT
post_cb = StationCatchmentChanged
[SDT_ENUM]
var = station.station_delivery_mode
type = SLE_UINT8
def = SD_NEAREST_FIRST
enumlist = _station_delivery_mode
str = STR_CONFIG_SETTING_CARGO_DELIVERY_MODE
strhelp = STR_CONFIG_SETTING_CARGO_DELIVERY_MODE_HELPTEXT
[SDT_BOOL]
var = order.gradual_loading
from = SLV_40

Loading…
Cancel
Save