diff options
-rw-r--r-- | src/cargopacket.cpp | 30 | ||||
-rw-r--r-- | src/cargopacket.h | 3 | ||||
-rw-r--r-- | src/saveload/station_sl.cpp | 1 | ||||
-rw-r--r-- | src/station_base.h | 4 | ||||
-rw-r--r-- | src/station_cmd.cpp | 55 |
5 files changed, 74 insertions, 19 deletions
diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index 16a5e61b1..2a4c96e82 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -659,33 +659,53 @@ uint StationCargoList::ShiftCargo(Taction action, StationID next, bool include_i /** * Truncates where each destination loses roughly the same percentage of its * cargo. This is done by randomizing the selection of packets to be removed. + * Optionally count the cargo by origin station. * @param max_move Maximum amount of cargo to remove. + * @param cargo_per_source Container for counting the cargo by origin. * @return Amount of cargo actually moved. */ -uint StationCargoList::Truncate(uint max_move) +uint StationCargoList::Truncate(uint max_move, StationCargoAmountMap *cargo_per_source) { max_move = min(max_move, this->count); uint prev_count = this->count; uint moved = 0; + uint loop = 0; + bool do_count = cargo_per_source != NULL; while (max_move > moved) { for (Iterator it(this->packets.begin()); it != this->packets.end();) { + CargoPacket *cp = *it; if (prev_count > max_move && RandomRange(prev_count) < prev_count - max_move) { + if (do_count && loop == 0) { + (*cargo_per_source)[cp->source] += cp->count; + } ++it; continue; } - CargoPacket *cp = *it; uint diff = max_move - moved; if (cp->count > diff) { - this->RemoveFromCache(cp, diff); - cp->Reduce(diff); - return moved + diff; + if (diff > 0) { + this->RemoveFromCache(cp, diff); + cp->Reduce(diff); + moved += diff; + } + if (loop > 0) { + if (do_count) (*cargo_per_source)[cp->source] -= diff; + return moved; + } else { + if (do_count) (*cargo_per_source)[cp->source] += cp->count; + ++it; + } } else { it = this->packets.erase(it); + if (do_count && loop > 0) { + (*cargo_per_source)[cp->source] -= cp->count; + } moved += cp->count; this->RemoveFromCache(cp, cp->count); delete cp; } } + loop++; } return moved; } diff --git a/src/cargopacket.h b/src/cargopacket.h index c31e4db58..dc1e66ba2 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -443,6 +443,7 @@ public: }; typedef MultiMap<StationID, CargoPacket *> StationCargoPacketMap; +typedef std::map<StationID, uint> StationCargoAmountMap; /** * CargoList that is used for stations. @@ -532,7 +533,7 @@ public: uint Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next); uint Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next); - uint Truncate(uint max_move = UINT_MAX); + uint Truncate(uint max_move = UINT_MAX, StationCargoAmountMap *cargo_per_source = NULL); uint Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge); /** diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 7c43cc7a8..8193bf623 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -282,6 +282,7 @@ const SaveLoad *GetGoodsDesc() SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, 183, SL_MAX_VERSION), SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, 183, SL_MAX_VERSION), SLEG_CONDVAR( _num_flows, SLE_UINT32, 183, SL_MAX_VERSION), + SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, 183, SL_MAX_VERSION), SLE_END() }; diff --git a/src/station_base.h b/src/station_base.h index b83c044a2..1eb5e0422 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -160,7 +160,8 @@ struct GoodsEntry { last_speed(0), last_age(255), link_graph(INVALID_LINK_GRAPH), - node(INVALID_NODE) + node(INVALID_NODE), + max_waiting_cargo(0) {} byte acceptance_pickup; ///< Status of this cargo, see #GoodsEntryStatus. @@ -197,6 +198,7 @@ struct GoodsEntry { LinkGraphID link_graph; ///< Link graph this station belongs to. NodeID node; ///< ID of node in link graph referring to this goods entry. FlowStatMap flows; ///< Planned flows through this station. + uint max_waiting_cargo; ///< Max cargo from this station waiting at any station. /** * Reports whether a vehicle has ever tried to load the cargo at this station. diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index d74c20668..958462b04 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3241,6 +3241,19 @@ static void UpdateStationRating(Station *st) int rating = 0; uint waiting = ge->cargo.TotalCount(); + /* num_dests is at least 1 if there is any cargo as + * INVALID_STATION is also a destination. + */ + uint num_dests = (uint)ge->cargo.Packets()->MapSize(); + + /* Average amount of cargo per next hop, but prefer solitary stations + * with only one or two next hops. They are allowed to have more + * cargo waiting per next hop. + * With manual cargo distribution waiting_avg = waiting / 2 as then + * INVALID_STATION is the only destination. + */ + uint waiting_avg = waiting / (num_dests + 1); + if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) { /* Perform custom station rating. If it succeeds the speed, days in transit and * waiting cargo ratings must not be executed. */ @@ -3248,7 +3261,7 @@ static void UpdateStationRating(Station *st) /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */ uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF; - uint32 var18 = min(ge->time_since_pickup, 0xFF) | (min(waiting, 0xFFFF) << 8) | (min(last_speed, 0xFF) << 24); + uint32 var18 = min(ge->time_since_pickup, 0xFF) | (min(ge->max_waiting_cargo, 0xFFFF) << 8) | (min(last_speed, 0xFF) << 24); /* Convert to the 'old' vehicle types */ uint32 var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10); uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs); @@ -3273,11 +3286,11 @@ static void UpdateStationRating(Station *st) (rating += 45, waittime > 3) || (rating += 35, true); - (rating -= 90, waiting > 1500) || - (rating += 55, waiting > 1000) || - (rating += 35, waiting > 600) || - (rating += 10, waiting > 300) || - (rating += 20, waiting > 100) || + (rating -= 90, ge->max_waiting_cargo > 1500) || + (rating += 55, ge->max_waiting_cargo > 1000) || + (rating += 35, ge->max_waiting_cargo > 600) || + (rating += 10, ge->max_waiting_cargo > 300) || + (rating += 20, ge->max_waiting_cargo > 100) || (rating += 10, true); } @@ -3295,12 +3308,12 @@ static void UpdateStationRating(Station *st) /* only modify rating in steps of -2, -1, 0, 1 or 2 */ ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2); - /* if rating is <= 64 and more than 200 items waiting, + /* if rating is <= 64 and more than 100 items waiting on average per destination, * remove some random amount of goods from the station */ - if (rating <= 64 && waiting >= 200) { + if (rating <= 64 && waiting_avg >= 100) { int dec = Random() & 0x1F; - if (waiting < 400) dec &= 7; - waiting -= dec + 1; + if (waiting_avg < 200) dec &= 7; + waiting -= (dec + 1) * num_dests; waiting_changed = true; } @@ -3309,7 +3322,7 @@ static void UpdateStationRating(Station *st) uint32 r = Random(); if (rating <= (int)GB(r, 0, 7)) { /* Need to have int, otherwise it will just overflow etc. */ - waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0); + waiting = max((int)waiting - (int)((GB(r, 8, 2) - 1) * num_dests), 0); waiting_changed = true; } } @@ -3332,7 +3345,25 @@ static void UpdateStationRating(Station *st) /* We can't truncate cargo that's already reserved for loading. * Thus StoredCount() here. */ if (waiting_changed && waiting < ge->cargo.AvailableCount()) { - ge->cargo.Truncate(ge->cargo.AvailableCount() - waiting); + /* Feed back the exact own waiting cargo at this station for the + * next rating calculation. */ + ge->max_waiting_cargo = 0; + + /* If truncating also punish the source stations' ratings to + * decrease the flow of incoming cargo. */ + + StationCargoAmountMap waiting_per_source; + ge->cargo.Truncate(ge->cargo.AvailableCount() - waiting, &waiting_per_source); + for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) { + Station *source_station = Station::GetIfValid(i->first); + if (source_station == NULL) continue; + + GoodsEntry &source_ge = source_station->goods[cs->Index()]; + source_ge.max_waiting_cargo = max(source_ge.max_waiting_cargo, i->second); + } + } else { + /* If the average number per next hop is low, be more forgiving. */ + ge->max_waiting_cargo = waiting_avg; } } } |