1
0
Fork 0

Feature: Improved logic of sharing industry production between 3 or more stations

pull/7929/head
dP 2020-01-10 00:39:35 +03:00 committed by Charles Pigott
parent b144258bf0
commit 1225693b9c
1 changed files with 84 additions and 43 deletions

View File

@ -4014,68 +4014,109 @@ const StationList *StationFinder::GetStations()
return &this->stations; return &this->stations;
} }
static bool CanMoveGoodsToStation(const Station *st, CargoID type)
{
/* Is the station reserved exclusively for somebody else? */
if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
/* Lowest possible rating, better not to give cargo anymore. */
if (st->goods[type].rating == 0) return false;
/* Selectively servicing stations, and not this one. */
if (_settings_game.order.selectgoods && !st->goods[type].HasVehicleEverTriedLoading()) return false;
if (IsCargoInClass(type, CC_PASSENGERS)) {
/* Passengers are never served by just a truck stop. */
if (st->facilities == FACIL_TRUCK_STOP) return false;
} else {
/* Non-passengers are never served by just a bus stop. */
if (st->facilities == FACIL_BUS_STOP) return false;
}
return true;
}
uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations) uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations)
{ {
/* Return if nothing to do. Also the rounding below fails for 0. */ /* Return if nothing to do. Also the rounding below fails for 0. */
if (all_stations->empty()) return 0;
if (amount == 0) return 0; if (amount == 0) return 0;
Station *st1 = nullptr; // Station with best rating Station *first_station = nullptr;
Station *st2 = nullptr; // Second best station typedef std::pair<Station *, uint> StationInfo;
uint best_rating1 = 0; // rating of st1 std::vector<StationInfo> used_stations;
uint best_rating2 = 0; // rating of st2
for (Station *st : *all_stations) { for (Station *st : *all_stations) {
/* Is the station reserved exclusively for somebody else? */ if (!CanMoveGoodsToStation(st, type)) continue;
if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
if (st->goods[type].rating == 0) continue; // Lowest possible rating, better not to give cargo anymore /* Avoid allocating a vector if there is only one station to significantly
* improve performance in this common case. */
if (_settings_game.order.selectgoods && !st->goods[type].HasVehicleEverTriedLoading()) continue; // Selectively servicing stations, and not this one if (first_station == nullptr) {
first_station = st;
if (IsCargoInClass(type, CC_PASSENGERS)) { continue;
if (st->facilities == FACIL_TRUCK_STOP) continue; // passengers are never served by just a truck stop
} else {
if (st->facilities == FACIL_BUS_STOP) continue; // non-passengers are never served by just a bus stop
} }
if (used_stations.empty()) {
/* This station can be used, add it to st1/st2 */ used_stations.reserve(2);
if (st1 == nullptr || st->goods[type].rating >= best_rating1) { used_stations.emplace_back(std::make_pair(first_station, 0));
st2 = st1; best_rating2 = best_rating1; st1 = st; best_rating1 = st->goods[type].rating;
} else if (st2 == nullptr || st->goods[type].rating >= best_rating2) {
st2 = st; best_rating2 = st->goods[type].rating;
} }
used_stations.emplace_back(std::make_pair(st, 0));
} }
/* no stations around at all? */ /* no stations around at all? */
if (st1 == nullptr) return 0; if (first_station == nullptr) return 0;
/* From now we'll calculate with fractal cargo amounts. if (used_stations.empty()) {
* First determine how much cargo we really have. */
amount *= best_rating1 + 1;
if (st2 == nullptr) {
/* only one station around */ /* only one station around */
return UpdateStationWaiting(st1, type, amount, source_type, source_id); amount *= first_station->goods[type].rating + 1;
return UpdateStationWaiting(first_station, type, amount, source_type, source_id);
} }
/* several stations around, the best two (highest rating) are in st1 and st2 */ uint company_best[OWNER_NONE + 1] = {}; // best rating for each company, including OWNER_NONE
assert(st1 != nullptr); uint company_sum[OWNER_NONE + 1] = {}; // sum of ratings for each company
assert(st2 != nullptr); uint best_rating = 0;
assert(best_rating1 != 0 || best_rating2 != 0); uint best_sum = 0; // sum of best ratings for each company
/* Then determine the amount the worst station gets. We do it this way as the for (auto &p : used_stations) {
* best should get a bonus, which in this case is the rounding difference from auto owner = p.first->owner;
* this calculation. In reality that will mean the bonus will be pretty low. auto rating = p.first->goods[type].rating;
* Nevertheless, the best station should always get the most cargo regardless if (rating > company_best[owner]) {
* of rounding issues. */ best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
uint worst_cargo = amount * best_rating2 / (best_rating1 + best_rating2); company_best[owner] = rating;
assert(worst_cargo <= (amount - worst_cargo)); if (rating > best_rating) best_rating = rating;
}
company_sum[owner] += rating;
}
/* And then send the cargo to the stations! */ /* From now we'll calculate with fractional cargo amounts.
uint moved = UpdateStationWaiting(st1, type, amount - worst_cargo, source_type, source_id); * First determine how much cargo we really have. */
/* These two UpdateStationWaiting's can't be in the statement as then the order amount *= best_rating + 1;
* of execution would be undefined and that could cause desyncs with callbacks. */
return moved + UpdateStationWaiting(st2, type, worst_cargo, source_type, source_id); uint moving = 0;
for (auto &p : used_stations) {
uint owner = p.first->owner;
/* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
* and by (station rating / sum of ratings in a company) to get the result for a single station. */
p.second = amount * company_best[owner] * p.first->goods[type].rating / best_sum / company_sum[owner];
moving += p.second;
}
/* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
if (amount > moving) {
std::sort(used_stations.begin(), used_stations.end(), [type] (const StationInfo &a, const StationInfo &b) {
return b.first->goods[type].rating < a.first->goods[type].rating;
});
assert(amount - moving <= used_stations.size());
for (uint i = 0; i < amount - moving; i++) {
used_stations[i].second++;
}
}
uint moved = 0;
for (auto &p : used_stations) {
moved += UpdateStationWaiting(p.first, type, p.second, source_type, source_id);
}
return moved;
} }
void UpdateStationDockingTiles(Station *st) void UpdateStationDockingTiles(Station *st)