Esempio n. 1
0
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;
}
Esempio n. 4
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();
}
Esempio n. 5
0
/**
 * 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;
}