Changeset - r24045:26feebf2c469
[Not reviewed]
master
0 1 0
dP - 4 years ago 2020-01-09 21:39:35
dp@dpointer.org
Feature: Improved logic of sharing industry production between 3 or more stations
1 file changed with 88 insertions and 47 deletions:
0 comments (0 inline, 0 general)
src/station_cmd.cpp
Show inline comments
 
@@ -4014,68 +4014,109 @@ const StationList *StationFinder::GetSta
 
	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)
 
{
 
	/* Return if nothing to do. Also the rounding below fails for 0. */
 
	if (all_stations->empty()) return 0;
 
	if (amount == 0) return 0;
 

	
 
	Station *st1 = nullptr;   // Station with best rating
 
	Station *st2 = nullptr;   // Second best station
 
	uint best_rating1 = 0; // rating of st1
 
	uint best_rating2 = 0; // rating of st2
 
	Station *first_station = nullptr;
 
	typedef std::pair<Station *, uint> StationInfo;
 
	std::vector<StationInfo> used_stations;
 

	
 
	for (Station *st : *all_stations) {
 
		/* Is the station reserved exclusively for somebody else? */
 
		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
 

	
 
		if (_settings_game.order.selectgoods && !st->goods[type].HasVehicleEverTriedLoading()) continue; // Selectively servicing stations, and not this one
 

	
 
		if (IsCargoInClass(type, CC_PASSENGERS)) {
 
			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 (!CanMoveGoodsToStation(st, type)) continue;
 

	
 
		/* Avoid allocating a vector if there is only one station to significantly
 
		 * improve performance in this common case. */
 
		if (first_station == nullptr) {
 
			first_station = st;
 
			continue;
 
		}
 

	
 
		/* This station can be used, add it to st1/st2 */
 
		if (st1 == nullptr || st->goods[type].rating >= best_rating1) {
 
			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;
 
		if  (used_stations.empty()) {
 
			used_stations.reserve(2);
 
			used_stations.emplace_back(std::make_pair(first_station, 0));
 
		}
 
		used_stations.emplace_back(std::make_pair(st, 0));
 
	}
 

	
 
	/* no stations around at all? */
 
	if (st1 == nullptr) return 0;
 

	
 
	/* From now we'll calculate with fractal cargo amounts.
 
	 * First determine how much cargo we really have. */
 
	amount *= best_rating1 + 1;
 

	
 
	if (st2 == nullptr) {
 
	if (first_station == nullptr) return 0;
 

	
 
	if (used_stations.empty()) {
 
		/* 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);
 
	}
 

	
 
	uint company_best[OWNER_NONE + 1] = {};  // best rating for each company, including OWNER_NONE
 
	uint company_sum[OWNER_NONE + 1] = {};   // sum of ratings for each company
 
	uint best_rating = 0;
 
	uint best_sum = 0;  // sum of best ratings for each company
 

	
 
	for (auto &p : used_stations) {
 
		auto owner = p.first->owner;
 
		auto rating = p.first->goods[type].rating;
 
		if (rating > company_best[owner]) {
 
			best_sum += rating - company_best[owner];  // it's usually faster than iterating companies later
 
			company_best[owner] = rating;
 
			if (rating > best_rating) best_rating = rating;
 
		}
 
		company_sum[owner] += rating;
 
	}
 

	
 
	/* several stations around, the best two (highest rating) are in st1 and st2 */
 
	assert(st1 != nullptr);
 
	assert(st2 != nullptr);
 
	assert(best_rating1 != 0 || best_rating2 != 0);
 

	
 
	/* Then determine the amount the worst station gets. We do it this way as the
 
	 * best should get a bonus, which in this case is the rounding difference from
 
	 * this calculation. In reality that will mean the bonus will be pretty low.
 
	 * Nevertheless, the best station should always get the most cargo regardless
 
	 * of rounding issues. */
 
	uint worst_cargo = amount * best_rating2 / (best_rating1 + best_rating2);
 
	assert(worst_cargo <= (amount - worst_cargo));
 

	
 
	/* And then send the cargo to the stations! */
 
	uint moved = UpdateStationWaiting(st1, type, amount - worst_cargo, source_type, source_id);
 
	/* These two UpdateStationWaiting's can't be in the statement as then the order
 
	 * of execution would be undefined and that could cause desyncs with callbacks. */
 
	return moved + UpdateStationWaiting(st2, type, worst_cargo, source_type, source_id);
 
	/* From now we'll calculate with fractional cargo amounts.
 
	 * First determine how much cargo we really have. */
 
	amount *= best_rating + 1;
 

	
 
	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)
0 comments (0 inline, 0 general)