void CargoList<Tinst>::Append(CargoPacket *cp) { assert(cp != NULL); static_cast<Tinst *>(this)->AddToCache(cp); for (List::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) { CargoPacket *icp = *it; if (Tinst::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) { icp->Merge(cp); return; } } /* The packet could not be merged with another one */ this->packets.push_back(cp); }
/** * 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, 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; } uint diff = max_move - moved; if (cp->count > 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; }
/** * Stages cargo for unloading. The cargo is sorted so that packets to be * transferred, delivered or kept are in consecutive chunks in the list. At the * same time the designation_counts are updated to reflect the size of those * chunks. * @param accepted If the cargo will be accepted at the station. * @param current_station ID of the station. * @param next_station ID of the station the vehicle will go to next. * @param order_flags OrderUnloadFlags that will apply to the unload operation. * @param ge GoodsEntry for getting the flows. * @param payment Payment object for registering transfers. * return If any cargo will be unloaded. */ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment) { this->AssertCountConsistency(); assert(this->action_counts[MTA_LOAD] == 0); this->action_counts[MTA_TRANSFER] = this->action_counts[MTA_DELIVER] = this->action_counts[MTA_KEEP] = 0; Iterator deliver = this->packets.end(); Iterator it = this->packets.begin(); uint sum = 0; bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0; bool force_unload = (order_flags & OUFB_UNLOAD) != 0; bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0; assert(this->count > 0 || it == this->packets.end()); while (sum < this->count) { CargoPacket *cp = *it; this->packets.erase(it++); StationID cargo_next = INVALID_STATION; MoveToAction action = MTA_LOAD; if (force_keep) { action = MTA_KEEP; } else if (force_unload && accepted && cp->source != current_station) { action = MTA_DELIVER; } else if (force_transfer) { action = MTA_TRANSFER; cargo_next = ge->GetVia(cp->source, current_station, next_station); assert((cargo_next != next_station || cargo_next == INVALID_STATION) && cargo_next != current_station); } else { /* Rewrite an invalid source station to some random other one to * avoid keeping the cargo in the vehicle forever. */ if (cp->source == INVALID_STATION && !ge->flows.empty()) { cp->source = ge->flows.begin()->first; } cargo_next = ge->GetVia(cp->source); if (cargo_next == INVALID_STATION) { action = (accepted && cp->source != current_station) ? MTA_DELIVER : MTA_KEEP; } else if (cargo_next == current_station) { action = MTA_DELIVER; } else if (cargo_next == next_station) { action = MTA_KEEP; } else { action = MTA_TRANSFER; } } Money share; switch (action) { case MTA_KEEP: this->packets.push_back(cp); if (deliver == this->packets.end()) --deliver; break; case MTA_DELIVER: this->packets.insert(deliver, cp); break; case MTA_TRANSFER: this->packets.push_front(cp); /* Add feeder share here to allow reusing field for next station. */ share = payment->PayTransfer(cp, cp->count); cp->AddFeederShare(share); this->feeder_share += share; cp->next_station = cargo_next; break; default: NOT_REACHED(); } this->action_counts[action] += cp->count; sum += cp->count; } this->AssertCountConsistency(); return this->action_counts[MTA_DELIVER] > 0 || this->action_counts[MTA_TRANSFER] > 0; }
bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer) { assert(mta == MTA_FINAL_DELIVERY || dest != NULL); assert(mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL); assert(st != INVALID_STATION || (mta != MTA_CARGO_LOAD && payment == NULL)); restart:; Iterator it(this->packets.begin()); while (it != this->packets.end() && max_move > 0) { CargoPacket *cp = *it; MoveToAction cp_mta = mta; OrderID current_next_order = cp->NextHop(); StationID current_next_unload = cp->NextStation(); if (cp_mta == MTA_CARGO_LOAD) { /* Invalid next hop but valid destination? Recompute next hop. */ if (current_next_order == INVALID_ORDER && cp->DestinationID() != INVALID_SOURCE) { if (!this->UpdateCargoNextHop(cp, Station::Get(st), cid)) { /* Failed to find destination, drop packet. */ it = this->packets.erase(it); continue; } current_next_order = cp->NextHop(); current_next_unload = cp->NextStation(); } /* Loading and not for the current vehicle? Skip. */ if (current_next_order != cur_order) { ++it; continue; } } /* Has this packet a destination and are we unloading to a station (not autoreplace)? */ if (cp->DestinationID() != INVALID_SOURCE && cp_mta != MTA_CARGO_LOAD && payment != NULL) { /* Not forced unload and not for unloading at this station? Skip the packet. */ if (cp_mta != MTA_UNLOAD && cp->NextStation() != INVALID_STATION && cp->NextStation() != st) { ++it; continue; } Station *station = Station::Get(st); bool found; StationID next_unload; RouteLink *link = FindRouteLinkForCargo(station, cid, cp, &next_unload, cur_order, &found); if (!found) { /* Sorry, link to destination vanished, make cargo disappear. */ static_cast<Tinst *>(this)->RemoveFromCache(cp); delete cp; it = this->packets.erase(it); continue; } if (link != NULL) { /* Not final destination. */ if (link->GetOriginOrderId() == cur_order && cp_mta != MTA_UNLOAD) { /* Cargo should stay on the vehicle and not forced unloading? Skip. */ ++it; continue; } /* Force transfer and update next hop. */ cp_mta = MTA_TRANSFER; current_next_order = link->GetOriginOrderId(); current_next_unload = next_unload; } else { /* Final destination, deliver. */ cp_mta = MTA_FINAL_DELIVERY; } } else if (cp_mta == MTA_NO_ACTION || (cp->source == st && cp_mta == MTA_FINAL_DELIVERY)) { /* Skip cargo that is not accepted or originated from this station. */ ++it; continue; } if (did_transfer != NULL && cp_mta == MTA_TRANSFER) *did_transfer = true; if (cp->count <= max_move) { /* Can move the complete packet */ max_move -= cp->count; it = this->packets.erase(it); static_cast<Tinst *>(this)->RemoveFromCache(cp); cp->next_order = current_next_order; cp->next_station = current_next_unload; switch (cp_mta) { case MTA_FINAL_DELIVERY: payment->PayFinalDelivery(cp, cp->count); delete cp; continue; // of the loop case MTA_CARGO_LOAD: cp->loaded_at_xy = Station::Get(st)->xy; break; case MTA_TRANSFER: cp->feeder_share += payment->PayTransfer(cp, cp->count); break; default: break; } dest->Append(cp); continue; } /* Can move only part of the packet */ if (cp_mta == MTA_FINAL_DELIVERY) { /* Final delivery doesn't need package splitting. */ payment->PayFinalDelivery(cp, max_move); /* Remove the delivered data from the cache */ uint left = cp->count - max_move; cp->count = max_move; static_cast<Tinst *>(this)->RemoveFromCache(cp); /* Final delivery payment pays the feeder share, so we have to * reset that so it is not 'shown' twice for partial unloads. */ cp->feeder_share = 0; cp->count = left; } else { /* But... the rest needs package splitting. */ CargoPacket *cp_new = cp->Split(max_move); /* We could not allocate a CargoPacket? Is the map that full? */ if (cp_new == NULL) return false; static_cast<Tinst *>(this)->RemoveFromCache(cp_new); // this reflects the changes in cp. cp_new->next_order = current_next_order; cp_new->next_station = current_next_unload; if (cp_mta == MTA_TRANSFER) { /* Add the feeder share before inserting in dest. */ cp_new->feeder_share += payment->PayTransfer(cp_new, max_move); } else if (cp_mta == MTA_CARGO_LOAD) { cp_new->loaded_at_xy = Station::Get(st)->xy; } dest->Append(cp_new); } max_move = 0; } if (max_move > 0 && mta == MTA_CARGO_LOAD && cur_order != INVALID_ORDER && Station::Get(st)->goods[cid].cargo.CountForNextHop(INVALID_ORDER) > 0) { /* We loaded all packets for the next hop, now load all packets without destination. */ cur_order = INVALID_ORDER; goto restart; } return it != packets.end(); }
/** * Stages cargo for unloading. The cargo is sorted so that packets to be * transferred, delivered or kept are in consecutive chunks in the list. At the * same time the designation_counts are updated to reflect the size of those * chunks. * @param accepted If the cargo will be accepted at the station. * @param current_station ID of the station. * @param next_station ID of the station the vehicle will go to next. * @param order_flags OrderUnloadFlags that will apply to the unload operation. * @param ge GoodsEntry for getting the flows. * @param payment Payment object for registering transfers. * return If any cargo will be unloaded. */ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment) { this->AssertCountConsistency(); assert(this->action_counts[MTA_LOAD] == 0); this->action_counts[MTA_TRANSFER] = this->action_counts[MTA_DELIVER] = this->action_counts[MTA_KEEP] = 0; Iterator deliver = this->packets.end(); Iterator it = this->packets.begin(); uint sum = 0; bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0; bool force_unload = (order_flags & OUFB_UNLOAD) != 0; bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0; assert(this->count > 0 || it == this->packets.end()); while (sum < this->count) { CargoPacket *cp = *it; this->packets.erase(it++); StationID cargo_next = INVALID_STATION; MoveToAction action = MTA_LOAD; if (force_keep) { action = MTA_KEEP; } else if (force_unload && accepted && cp->source != current_station) { action = MTA_DELIVER; } else if (force_transfer) { action = MTA_TRANSFER; /* We cannot send the cargo to any of the possible next hops and * also not to the current station. */ FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source)); if (flow_it == ge->flows.end()) { cargo_next = INVALID_STATION; } else { FlowStat new_shares = flow_it->second; new_shares.ChangeShare(current_station, INT_MIN); StationIDStack excluded = next_station; while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) { new_shares.ChangeShare(excluded.Pop(), INT_MIN); } if (new_shares.GetShares()->empty()) { cargo_next = INVALID_STATION; } else { cargo_next = new_shares.GetVia(); } } } else { /* Rewrite an invalid source station to some random other one to * avoid keeping the cargo in the vehicle forever. */ if (cp->source == INVALID_STATION && !ge->flows.empty()) { cp->source = ge->flows.begin()->first; } bool restricted = false; FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source)); if (flow_it == ge->flows.end()) { cargo_next = INVALID_STATION; } else { cargo_next = flow_it->second.GetViaWithRestricted(restricted); } action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station); if (restricted && action == MTA_TRANSFER) { /* If the flow is restricted we can't transfer to it. Choose an * unrestricted one instead. */ cargo_next = flow_it->second.GetVia(); action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station); } } Money share; switch (action) { case MTA_KEEP: this->packets.push_back(cp); if (deliver == this->packets.end()) --deliver; break; case MTA_DELIVER: this->packets.insert(deliver, cp); break; case MTA_TRANSFER: this->packets.push_front(cp); /* Add feeder share here to allow reusing field for next station. */ share = payment->PayTransfer(cp, cp->count); cp->AddFeederShare(share); this->feeder_share += share; cp->next_station = cargo_next; break; default: NOT_REACHED(); } this->action_counts[action] += cp->count; sum += cp->count; } this->AssertCountConsistency(); return this->action_counts[MTA_DELIVER] > 0 || this->action_counts[MTA_TRANSFER] > 0; }