mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r25012) -Codechange: persistently keep 'reserved' cargo (for full-load improved loading) instead of calculating if for every cycle
This commit is contained in:
parent
446d50f658
commit
cdb0a76958
|
@ -106,7 +106,7 @@ void CheckCargoCapacity(Vehicle *v)
|
|||
if (dest->cargo.Count() >= dest->cargo_cap || dest->cargo_type != src->cargo_type) continue;
|
||||
|
||||
uint amount = min(to_spread, dest->cargo_cap - dest->cargo.Count());
|
||||
src->cargo.MoveTo(&dest->cargo, amount, VehicleCargoList::MTA_UNLOAD, NULL);
|
||||
src->cargo.Shift(amount, &dest->cargo);
|
||||
to_spread -= amount;
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chai
|
|||
uint amount = min(src->cargo.Count(), dest->cargo_cap - dest->cargo.Count());
|
||||
if (amount <= 0) continue;
|
||||
|
||||
src->cargo.MoveTo(&dest->cargo, amount, VehicleCargoList::MTA_UNLOAD, NULL);
|
||||
src->cargo.Shift(amount, &dest->cargo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,11 +42,9 @@ uint CargoRemoval<Tsource>::Preprocess(CargoPacket *cp)
|
|||
{
|
||||
if (this->max_move >= cp->Count()) {
|
||||
this->max_move -= cp->Count();
|
||||
this->source->RemoveFromCache(cp, cp->Count());
|
||||
return cp->Count();
|
||||
} else {
|
||||
uint ret = this->max_move;
|
||||
this->source->RemoveFromCache(cp, ret);
|
||||
this->max_move = 0;
|
||||
return ret;
|
||||
}
|
||||
|
@ -70,6 +68,34 @@ bool CargoRemoval<Tsource>::Postprocess(CargoPacket *cp, uint remove)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes some cargo from a StationCargoList.
|
||||
* @param cp Packet to be removed.
|
||||
* @return True if the packet was completely delivered, false if only part of
|
||||
* it was.
|
||||
*/
|
||||
template<>
|
||||
bool CargoRemoval<StationCargoList>::operator()(CargoPacket *cp)
|
||||
{
|
||||
uint remove = this->Preprocess(cp);
|
||||
this->source->RemoveFromCache(cp, remove);
|
||||
return this->Postprocess(cp, remove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes some cargo from a VehicleCargoList.
|
||||
* @param cp Packet to be removed.
|
||||
* @return True if the packet was completely delivered, false if only part of
|
||||
* it was.
|
||||
*/
|
||||
template<>
|
||||
bool CargoRemoval<VehicleCargoList>::operator()(CargoPacket *cp)
|
||||
{
|
||||
uint remove = this->Preprocess(cp);
|
||||
this->source->RemoveFromMeta(cp, VehicleCargoList::MTA_KEEP, remove);
|
||||
return this->Postprocess(cp, remove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers some cargo.
|
||||
* @param cp Packet to be delivered.
|
||||
|
@ -79,6 +105,7 @@ bool CargoRemoval<Tsource>::Postprocess(CargoPacket *cp, uint remove)
|
|||
bool CargoDelivery::operator()(CargoPacket *cp)
|
||||
{
|
||||
uint remove = this->Preprocess(cp);
|
||||
this->source->RemoveFromMeta(cp, VehicleCargoList::MTA_DELIVER, remove);
|
||||
this->payment->PayFinalDelivery(cp, remove);
|
||||
return this->Postprocess(cp, remove);
|
||||
}
|
||||
|
@ -94,6 +121,38 @@ bool CargoLoad::operator()(CargoPacket *cp)
|
|||
if (cp_new == NULL) return false;
|
||||
cp_new->SetLoadPlace(this->load_place);
|
||||
this->source->RemoveFromCache(cp_new, cp_new->Count());
|
||||
this->destination->Append(cp_new, VehicleCargoList::MTA_KEEP);
|
||||
return cp_new == cp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserves some cargo for loading.
|
||||
* @param cp Packet to be reserved.
|
||||
* @return True if the packet was completely reserved, false if part of it was.
|
||||
*/
|
||||
bool CargoReservation::operator()(CargoPacket *cp)
|
||||
{
|
||||
CargoPacket *cp_new = this->Preprocess(cp);
|
||||
if (cp_new == NULL) return false;
|
||||
cp_new->SetLoadPlace(this->load_place);
|
||||
this->source->reserved_count += cp_new->Count();
|
||||
this->source->RemoveFromCache(cp_new, cp_new->Count());
|
||||
this->destination->Append(cp_new, VehicleCargoList::MTA_LOAD);
|
||||
return cp_new == cp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns some reserved cargo.
|
||||
* @param cp Packet to be returned.
|
||||
* @return True if the packet was completely returned, false if part of it was.
|
||||
*/
|
||||
bool CargoReturn::operator()(CargoPacket *cp)
|
||||
{
|
||||
CargoPacket *cp_new = this->Preprocess(cp);
|
||||
if (cp_new == NULL) cp_new = cp;
|
||||
assert(cp_new->Count() <= this->destination->reserved_count);
|
||||
this->source->RemoveFromMeta(cp_new, VehicleCargoList::MTA_LOAD, cp_new->Count());
|
||||
this->destination->reserved_count -= cp_new->Count();
|
||||
this->destination->Append(cp_new);
|
||||
return cp_new == cp;
|
||||
}
|
||||
|
@ -107,7 +166,7 @@ bool CargoTransfer::operator()(CargoPacket *cp)
|
|||
{
|
||||
CargoPacket *cp_new = this->Preprocess(cp);
|
||||
if (cp_new == NULL) return false;
|
||||
this->source->RemoveFromCache(cp_new, cp_new->Count());
|
||||
this->source->RemoveFromMeta(cp_new, VehicleCargoList::MTA_TRANSFER, cp_new->Count());
|
||||
this->destination->Append(cp_new);
|
||||
return cp_new == cp;
|
||||
}
|
||||
|
@ -121,8 +180,8 @@ bool CargoShift::operator()(CargoPacket *cp)
|
|||
{
|
||||
CargoPacket *cp_new = this->Preprocess(cp);
|
||||
if (cp_new == NULL) cp_new = cp;
|
||||
this->source->RemoveFromCache(cp_new, cp_new->Count());
|
||||
this->destination->Append(cp_new);
|
||||
this->source->RemoveFromMeta(cp_new, VehicleCargoList::MTA_KEEP, cp_new->Count());
|
||||
this->destination->Append(cp_new, VehicleCargoList::MTA_KEEP);
|
||||
return cp_new == cp;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,13 +34,7 @@ public:
|
|||
*/
|
||||
uint MaxMove() { return this->max_move; }
|
||||
|
||||
/**
|
||||
* Removes some cargo.
|
||||
* @param cp Packet to be removed.
|
||||
* @return True if the packet was completely delivered, false if only part of
|
||||
* it was.
|
||||
*/
|
||||
inline bool operator()(CargoPacket *cp) { return this->Postprocess(cp, this->Preprocess(cp)); }
|
||||
bool operator()(CargoPacket *cp);
|
||||
};
|
||||
|
||||
/** Action of final delivery of cargo. */
|
||||
|
@ -95,6 +89,22 @@ public:
|
|||
bool operator()(CargoPacket *cp);
|
||||
};
|
||||
|
||||
/** Action of reserving cargo from a station to be loaded onto a vehicle. */
|
||||
class CargoReservation: public CargoLoad {
|
||||
public:
|
||||
CargoReservation(StationCargoList *source, VehicleCargoList *destination, uint max_move, TileIndex load_place) :
|
||||
CargoLoad(source, destination, max_move, load_place) {}
|
||||
bool operator()(CargoPacket *cp);
|
||||
};
|
||||
|
||||
/** Action of returning previously reserved cargo from the vehicle to the station. */
|
||||
class CargoReturn: public CargoMovement<VehicleCargoList, StationCargoList> {
|
||||
public:
|
||||
CargoReturn(VehicleCargoList *source, StationCargoList *destination, uint max_move) :
|
||||
CargoMovement<VehicleCargoList, StationCargoList>(source, destination, max_move) {}
|
||||
bool operator()(CargoPacket *cp);
|
||||
};
|
||||
|
||||
/** Action of shifting cargo from one vehicle to another. */
|
||||
class CargoShift : public CargoMovement<VehicleCargoList, VehicleCargoList> {
|
||||
public:
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "core/pool_func.hpp"
|
||||
#include "economy_base.h"
|
||||
#include "cargoaction.h"
|
||||
#include "order_type.h"
|
||||
|
||||
/* Initialize the cargopacket-pool */
|
||||
CargoPacketPool _cargopacket_pool("CargoPacket");
|
||||
|
@ -83,7 +84,7 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
|
|||
* @param new_size Size of the split part.
|
||||
* @return Split off part, or NULL if no packet could be allocated!
|
||||
*/
|
||||
inline CargoPacket *CargoPacket::Split(uint new_size)
|
||||
CargoPacket *CargoPacket::Split(uint new_size)
|
||||
{
|
||||
if (!CargoPacket::CanAllocateItem()) return NULL;
|
||||
|
||||
|
@ -98,7 +99,7 @@ inline CargoPacket *CargoPacket::Split(uint new_size)
|
|||
* Merge another packet into this one.
|
||||
* @param cp Packet to be merged in.
|
||||
*/
|
||||
inline void CargoPacket::Merge(CargoPacket *cp)
|
||||
void CargoPacket::Merge(CargoPacket *cp)
|
||||
{
|
||||
this->count += cp->count;
|
||||
this->feeder_share += cp->feeder_share;
|
||||
|
@ -109,7 +110,7 @@ inline void CargoPacket::Merge(CargoPacket *cp)
|
|||
* Reduce the packet by the given amount and remove the feeder share.
|
||||
* @param count Amount to be removed.
|
||||
*/
|
||||
inline void CargoPacket::Reduce(uint count)
|
||||
void CargoPacket::Reduce(uint count)
|
||||
{
|
||||
assert(count < this->count);
|
||||
this->feeder_share -= this->FeederShare(count);
|
||||
|
@ -194,31 +195,9 @@ void CargoList<Tinst>::AddToCache(const CargoPacket *cp)
|
|||
this->cargo_days_in_transit += cp->days_in_transit * cp->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given cargo packet. Tries to merge it with another one in the
|
||||
* packets list. If no fitting packet is found, appends it.
|
||||
* @warning After appending this packet may not exist anymore!
|
||||
* @note Do not use the cargo packet anymore after it has been appended to this CargoList!
|
||||
* @param cp Cargo packet to add.
|
||||
* @pre cp != NULL
|
||||
*/
|
||||
template <class Tinst>
|
||||
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++) {
|
||||
if (CargoList<Tinst>::TryMerge(*it, cp)) return;
|
||||
}
|
||||
|
||||
/* The packet could not be merged with another one */
|
||||
this->packets.push_back(cp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the cargo in this list to the given amount. It leaves the
|
||||
* first count cargo entities and removes the rest.
|
||||
* first cargo entities and removes max_move from the back of the list.
|
||||
* @param max_move Maximum amount of entities to be removed from the list.
|
||||
* @return Amount of entities actually moved.
|
||||
*/
|
||||
|
@ -230,102 +209,6 @@ uint CargoList<Tinst>::Truncate(uint max_move)
|
|||
return max_move;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the given amount of cargo to another list.
|
||||
* Depending on the value of mta the side effects of this function differ:
|
||||
* - MTA_FINAL_DELIVERY: Destroys the packets that do not originate from a specific station.
|
||||
* - MTA_CARGO_LOAD: Sets the loaded_at_xy value of the moved packets.
|
||||
* - MTA_TRANSFER: Just move without side effects.
|
||||
* - MTA_UNLOAD: Just move without side effects.
|
||||
* @param dest Destination to move the cargo to.
|
||||
* @param max_move Amount of cargo entities to move.
|
||||
* @param mta How to handle the moving (side effects).
|
||||
* @param data Depending on mta the data of this variable differs:
|
||||
* - MTA_FINAL_DELIVERY - Station ID of packet's origin not to remove.
|
||||
* - MTA_CARGO_LOAD - Station's tile index of load.
|
||||
* - MTA_TRANSFER - Unused.
|
||||
* - MTA_UNLOAD - Unused.
|
||||
* @param payment The payment helper.
|
||||
*
|
||||
* @pre mta == MTA_FINAL_DELIVERY || dest != NULL
|
||||
* @pre mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL
|
||||
* @return True if there are still packets that might be moved from this cargo list.
|
||||
*/
|
||||
template <class Tinst>
|
||||
template <class Tother_inst>
|
||||
bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta, CargoPayment *payment, uint data)
|
||||
{
|
||||
assert(mta == MTA_FINAL_DELIVERY || dest != NULL);
|
||||
assert(mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL);
|
||||
|
||||
Iterator it(this->packets.begin());
|
||||
while (it != this->packets.end() && max_move > 0) {
|
||||
CargoPacket *cp = *it;
|
||||
if (cp->source == data && mta == MTA_FINAL_DELIVERY) {
|
||||
/* Skip cargo that originated from this station. */
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
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->count);
|
||||
switch (mta) {
|
||||
case MTA_FINAL_DELIVERY:
|
||||
payment->PayFinalDelivery(cp, cp->count);
|
||||
delete cp;
|
||||
continue; // of the loop
|
||||
|
||||
case MTA_CARGO_LOAD:
|
||||
cp->loaded_at_xy = data;
|
||||
break;
|
||||
|
||||
case MTA_TRANSFER:
|
||||
cp->feeder_share += payment->PayTransfer(cp, cp->count);
|
||||
break;
|
||||
|
||||
case MTA_UNLOAD:
|
||||
break;
|
||||
}
|
||||
dest->Append(cp);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Can move only part of the packet */
|
||||
if (mta == MTA_FINAL_DELIVERY) {
|
||||
/* Final delivery doesn't need package splitting. */
|
||||
payment->PayFinalDelivery(cp, max_move);
|
||||
|
||||
/* Remove the delivered data from the cache */
|
||||
static_cast<Tinst *>(this)->RemoveFromCache(cp, max_move);
|
||||
cp->Reduce(max_move);
|
||||
} 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, max_move); // this reflects the changes in cp.
|
||||
|
||||
if (mta == MTA_TRANSFER) {
|
||||
/* Add the feeder share before inserting in dest. */
|
||||
cp_new->feeder_share += payment->PayTransfer(cp_new, max_move);
|
||||
} else if (mta == MTA_CARGO_LOAD) {
|
||||
cp_new->loaded_at_xy = data;
|
||||
}
|
||||
|
||||
dest->Append(cp_new);
|
||||
}
|
||||
|
||||
max_move = 0;
|
||||
}
|
||||
|
||||
return it != packets.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts cargo from the front of the packet list and applies some action to it.
|
||||
* @tparam Taction Action class or function to be used. It should define
|
||||
|
@ -416,6 +299,47 @@ template <class Tinst>
|
|||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Appends the given cargo packet. Tries to merge it with another one in the
|
||||
* packets list. If no fitting packet is found, appends it. You can only append
|
||||
* packets to the ranges of packets designated for keeping or loading.
|
||||
* Furthermore if there are already packets reserved for loading you cannot
|
||||
* directly add packets to the "keep" list. You first have to load the reserved
|
||||
* ones.
|
||||
* @warning After appending this packet may not exist anymore!
|
||||
* @note Do not use the cargo packet anymore after it has been appended to this CargoList!
|
||||
* @param cp Cargo packet to add.
|
||||
* @param action Either MTA_KEEP if you want to add the packet directly or MTA_LOAD
|
||||
* if you want to reserve it first.
|
||||
* @pre cp != NULL
|
||||
* @pre action == MTA_LOAD || (action == MTA_KEEP && this->designation_counts[MTA_LOAD] == 0)
|
||||
*/
|
||||
void VehicleCargoList::Append(CargoPacket *cp, MoveToAction action)
|
||||
{
|
||||
assert(cp != NULL);
|
||||
assert(action == MTA_LOAD ||
|
||||
(action == MTA_KEEP && this->action_counts[MTA_LOAD] == 0));
|
||||
this->AddToMeta(cp, action);
|
||||
|
||||
if (this->count == cp->count) {
|
||||
this->packets.push_back(cp);
|
||||
return;
|
||||
}
|
||||
|
||||
uint sum = cp->count;
|
||||
for (ReverseIterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
|
||||
CargoPacket *icp = *it;
|
||||
if (VehicleCargoList::TryMerge(icp, cp)) return;
|
||||
sum += icp->count;
|
||||
if (sum >= this->action_counts[action]) {
|
||||
this->packets.push_back(cp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cached values to reflect the removal of this packet or part of it.
|
||||
* Decreases count, feeder share and days_in_transit.
|
||||
|
@ -439,6 +363,33 @@ void VehicleCargoList::AddToCache(const CargoPacket *cp)
|
|||
this->Parent::AddToCache(cp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a packet or part of it from the metadata.
|
||||
* @param cp Packet to be removed.
|
||||
* @param action MoveToAction of the packet (for updating the counts).
|
||||
* @param count Amount of cargo to be removed.
|
||||
*/
|
||||
void VehicleCargoList::RemoveFromMeta(const CargoPacket *cp, MoveToAction action, uint count)
|
||||
{
|
||||
this->AssertCountConsistency();
|
||||
this->RemoveFromCache(cp, count);
|
||||
this->action_counts[action] -= count;
|
||||
this->AssertCountConsistency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a packet to the metadata.
|
||||
* @param cp Packet to be added.
|
||||
* @param action MoveToAction of the packet.
|
||||
*/
|
||||
void VehicleCargoList::AddToMeta(const CargoPacket *cp, MoveToAction action)
|
||||
{
|
||||
this->AssertCountConsistency();
|
||||
this->AddToCache(cp);
|
||||
this->action_counts[action] += cp->count;
|
||||
this->AssertCountConsistency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ages the all cargo in this list.
|
||||
*/
|
||||
|
@ -454,6 +405,44 @@ void VehicleCargoList::AgeCargo()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 order_flags OrderUnloadFlags that will apply to the unload operation.
|
||||
* return If any cargo will be unloaded.
|
||||
*/
|
||||
bool VehicleCargoList::Stage(bool accepted, StationID current_station, uint8 order_flags)
|
||||
{
|
||||
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;
|
||||
while (sum < this->count) {
|
||||
CargoPacket *cp = *it;
|
||||
this->packets.erase(it++);
|
||||
if ((order_flags & OUFB_TRANSFER) != 0 || (!accepted && (order_flags & OUFB_UNLOAD) != 0)) {
|
||||
this->packets.push_front(cp);
|
||||
this->action_counts[MTA_TRANSFER] += cp->count;
|
||||
} else if (accepted && current_station != cp->source && (order_flags & OUFB_NO_UNLOAD) == 0) {
|
||||
this->packets.insert(deliver, cp);
|
||||
this->action_counts[MTA_DELIVER] += cp->count;
|
||||
} else {
|
||||
this->packets.push_back(cp);
|
||||
if (deliver == this->packets.end()) --deliver;
|
||||
this->action_counts[MTA_KEEP] += cp->count;
|
||||
}
|
||||
sum += cp->count;
|
||||
}
|
||||
this->AssertCountConsistency();
|
||||
return this->action_counts[MTA_DELIVER] > 0 || this->action_counts[MTA_TRANSFER] > 0;
|
||||
}
|
||||
|
||||
/** Invalidates the cached data and rebuild it. */
|
||||
void VehicleCargoList::InvalidateCache()
|
||||
{
|
||||
|
@ -461,15 +450,135 @@ void VehicleCargoList::InvalidateCache()
|
|||
this->Parent::InvalidateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves some cargo from one designation to another. You can only move
|
||||
* between adjacent designations. E.g. you can keep cargo that was
|
||||
* previously reserved (MTA_LOAD) or you can mark cargo to be transferred
|
||||
* that was previously marked as to be delivered, but you can't reserve
|
||||
* cargo that's marked as to be delivered.
|
||||
*/
|
||||
uint VehicleCargoList::Reassign(uint max_move, MoveToAction from, MoveToAction to)
|
||||
{
|
||||
max_move = min(this->action_counts[from], max_move);
|
||||
assert(Delta((int)from, (int)to) == 1);
|
||||
this->action_counts[from] -= max_move;
|
||||
this->action_counts[to] += max_move;
|
||||
return max_move;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns reserved cargo to the station and removes it from the cache.
|
||||
* @param dest Station the cargo is returned to.
|
||||
* @param max_move Maximum amount of cargo to move.
|
||||
* @return Amount of cargo actually returned.
|
||||
*/
|
||||
uint VehicleCargoList::Return(uint max_move, StationCargoList *dest)
|
||||
{
|
||||
max_move = min(this->action_counts[MTA_LOAD], max_move);
|
||||
this->PopCargo(CargoReturn(this, dest, max_move));
|
||||
return max_move;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts cargo between two vehicles.
|
||||
* @param dest Other vehicle's cargo list.
|
||||
* @param max_move Maximum amount of cargo to be moved.
|
||||
* @return Amount of cargo actually moved.
|
||||
*/
|
||||
uint VehicleCargoList::Shift(uint max_move, VehicleCargoList *dest)
|
||||
{
|
||||
max_move = min(this->count, max_move);
|
||||
this->PopCargo(CargoShift(this, dest, max_move));
|
||||
return max_move;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads cargo at the given station. Deliver or transfer, depending on the
|
||||
* ranges defined by designation_counts.
|
||||
* @param dest StationCargoList to add transferred cargo to.
|
||||
* @param max_move Maximum amount of cargo to move.
|
||||
* @param payment Payment object to register payments in.
|
||||
* @return Amount of cargo actually unloaded.
|
||||
*/
|
||||
uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPayment *payment)
|
||||
{
|
||||
uint moved = 0;
|
||||
if (this->action_counts[MTA_TRANSFER] > 0) {
|
||||
uint move = min(this->action_counts[MTA_TRANSFER], max_move);
|
||||
this->ShiftCargo(CargoTransfer(this, dest, move, payment));
|
||||
moved += move;
|
||||
}
|
||||
if (this->action_counts[MTA_TRANSFER] == 0 && this->action_counts[MTA_DELIVER] > 0 && moved < max_move) {
|
||||
uint move = min(this->action_counts[MTA_DELIVER], max_move - moved);
|
||||
this->ShiftCargo(CargoDelivery(this, move, payment));
|
||||
moved += move;
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Station cargo list implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Appends the given cargo packet. Tries to merge it with another one in the
|
||||
* packets list. If no fitting packet is found, appends it.
|
||||
* @warning After appending this packet may not exist anymore!
|
||||
* @note Do not use the cargo packet anymore after it has been appended to this CargoList!
|
||||
* @param cp Cargo packet to add.
|
||||
* @pre cp != NULL
|
||||
*/
|
||||
void StationCargoList::Append(CargoPacket *cp)
|
||||
{
|
||||
assert(cp != NULL);
|
||||
this->AddToCache(cp);
|
||||
|
||||
for (List::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
|
||||
if (StationCargoList::TryMerge(*it, cp)) return;
|
||||
}
|
||||
|
||||
/* The packet could not be merged with another one */
|
||||
this->packets.push_back(cp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserves cargo for loading onto the vehicle.
|
||||
* @param dest VehicleCargoList to reserve for.
|
||||
* @param max_move Maximum amount of cargo to reserve.
|
||||
* @param load_place Tile index of the current station.
|
||||
* @return Amount of cargo actually reserved.
|
||||
*/
|
||||
uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place)
|
||||
{
|
||||
max_move = min(this->count, max_move);
|
||||
this->ShiftCargo(CargoReservation(this, dest, max_move, load_place));
|
||||
return max_move;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads cargo onto a vehicle. If the vehicle has reserved cargo load that.
|
||||
* Otherwise load cargo from the station.
|
||||
* @param dest Vehicle cargo list where the cargo resides.
|
||||
* @param max_move Amount of cargo to load.
|
||||
* @return Amount of cargo actually loaded.
|
||||
*/
|
||||
uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place)
|
||||
{
|
||||
uint move = min(dest->ActionCount(VehicleCargoList::MTA_LOAD), max_move);
|
||||
if (move > 0) {
|
||||
this->reserved_count -= move;
|
||||
dest->Reassign(move, VehicleCargoList::MTA_LOAD, VehicleCargoList::MTA_KEEP);
|
||||
} else {
|
||||
move = min(this->count, max_move);
|
||||
this->ShiftCargo(CargoLoad(this, dest, move, load_place));
|
||||
}
|
||||
return move;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to instantiate everything we want to be usable.
|
||||
*/
|
||||
template class CargoList<VehicleCargoList>;
|
||||
template class CargoList<StationCargoList>;
|
||||
|
||||
/** Autoreplace Vehicle -> Vehicle 'transfer'. */
|
||||
template bool CargoList<VehicleCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
|
||||
/** Cargo unloading at a station. */
|
||||
template bool CargoList<VehicleCargoList>::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
|
||||
/** Cargo loading at a station. */
|
||||
template bool CargoList<StationCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
|
||||
|
|
|
@ -29,6 +29,7 @@ typedef Pool<CargoPacket, CargoPacketID, 1024, 0xFFF000, PT_NORMAL, true, false>
|
|||
extern CargoPacketPool _cargopacket_pool;
|
||||
|
||||
template <class Tinst> class CargoList;
|
||||
class StationCargoList; // forward-declare, so we can use it in VehicleCargoList.
|
||||
extern const struct SaveLoad *GetCargoPacketDesc();
|
||||
|
||||
/**
|
||||
|
@ -203,10 +204,13 @@ public:
|
|||
|
||||
/** Kind of actions that could be done with packets on move. */
|
||||
enum MoveToAction {
|
||||
MTA_FINAL_DELIVERY, ///< "Deliver" the packet to the final destination, i.e. destroy the packet.
|
||||
MTA_CARGO_LOAD, ///< Load the packet onto a vehicle, i.e. set the last loaded station ID.
|
||||
MTA_TRANSFER, ///< The cargo is moved as part of a transfer.
|
||||
MTA_UNLOAD, ///< The cargo is moved as part of a forced unload.
|
||||
MTA_BEGIN = 0,
|
||||
MTA_TRANSFER = 0, ///< Transfer the cargo to the station.
|
||||
MTA_DELIVER, ///< Deliver the cargo to some town or industry.
|
||||
MTA_KEEP, ///< Keep the cargo in the vehicle.
|
||||
MTA_LOAD, ///< Load the cargo from the station.
|
||||
MTA_END,
|
||||
NUM_MOVE_TO_ACTION = MTA_END
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -280,13 +284,8 @@ public:
|
|||
return this->count == 0 ? 0 : this->cargo_days_in_transit / this->count;
|
||||
}
|
||||
|
||||
|
||||
void Append(CargoPacket *cp);
|
||||
uint Truncate(uint max_move = UINT_MAX);
|
||||
|
||||
template <class Tother_inst>
|
||||
bool MoveTo(Tother_inst *dest, uint count, MoveToAction mta, CargoPayment *payment, uint data = 0);
|
||||
|
||||
void InvalidateCache();
|
||||
};
|
||||
|
||||
|
@ -298,11 +297,26 @@ protected:
|
|||
/** The (direct) parent of this class. */
|
||||
typedef CargoList<VehicleCargoList> Parent;
|
||||
|
||||
Money feeder_share; ///< Cache for the feeder share.
|
||||
Money feeder_share; ///< Cache for the feeder share.
|
||||
uint action_counts[NUM_MOVE_TO_ACTION]; ///< Counts of cargo to be transfered, delivered, kept and loaded.
|
||||
|
||||
/**
|
||||
* Assert that the designation counts add up.
|
||||
*/
|
||||
inline void AssertCountConsistency() const
|
||||
{
|
||||
assert(this->action_counts[MTA_KEEP] +
|
||||
this->action_counts[MTA_DELIVER] +
|
||||
this->action_counts[MTA_TRANSFER] +
|
||||
this->action_counts[MTA_LOAD] == this->count);
|
||||
}
|
||||
|
||||
void AddToCache(const CargoPacket *cp);
|
||||
void RemoveFromCache(const CargoPacket *cp, uint count);
|
||||
|
||||
void AddToMeta(const CargoPacket *cp, MoveToAction action);
|
||||
void RemoveFromMeta(const CargoPacket *cp, MoveToAction action, uint count);
|
||||
|
||||
public:
|
||||
/** The super class ought to know what it's doing. */
|
||||
friend class CargoList<VehicleCargoList>;
|
||||
|
@ -314,6 +328,7 @@ public:
|
|||
friend class CargoDelivery;
|
||||
template<class Tsource>
|
||||
friend class CargoRemoval;
|
||||
friend class CargoReturn;
|
||||
|
||||
/**
|
||||
* Returns total sum of the feeder share for all packets.
|
||||
|
@ -324,10 +339,72 @@ public:
|
|||
return this->feeder_share;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of cargo designated for a given purpose.
|
||||
* @param action Action the cargo is designated for.
|
||||
* @return Amount of cargo designated for the given action.
|
||||
*/
|
||||
inline uint ActionCount(MoveToAction action) const
|
||||
{
|
||||
return this->action_counts[action];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of cargo on board the vehicle (ie not only
|
||||
* reserved).
|
||||
* @return Cargo on board the vehicle.
|
||||
*/
|
||||
inline uint OnboardCount() const
|
||||
{
|
||||
return this->count - this->action_counts[MTA_LOAD];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of cargo to be moved out of the vehicle at the current station.
|
||||
* @return Cargo to be moved.
|
||||
*/
|
||||
inline uint UnloadCount() const
|
||||
{
|
||||
return this->action_counts[MTA_TRANSFER] + this->action_counts[MTA_DELIVER];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of cargo to be kept in the vehicle at the current station.
|
||||
* @return Cargo to be kept or loaded.
|
||||
*/
|
||||
inline uint RemainingCount() const
|
||||
{
|
||||
return this->action_counts[MTA_KEEP] + this->action_counts[MTA_LOAD];
|
||||
}
|
||||
|
||||
void Append(CargoPacket *cp, MoveToAction action = MTA_KEEP);
|
||||
|
||||
void AgeCargo();
|
||||
|
||||
void InvalidateCache();
|
||||
|
||||
bool Stage(bool accepted, StationID current_station, uint8 order_flags);
|
||||
|
||||
/**
|
||||
* Marks all cargo in the vehicle as to be kept. This is mostly useful for
|
||||
* loading old savegames. When loading is aborted the reserved cargo has
|
||||
* to be returned first.
|
||||
*/
|
||||
inline void KeepAll()
|
||||
{
|
||||
this->action_counts[MTA_DELIVER] = this->action_counts[MTA_TRANSFER] = this->action_counts[MTA_LOAD] = 0;
|
||||
this->action_counts[MTA_KEEP] = this->count;
|
||||
}
|
||||
|
||||
/* Methods for moving cargo around. First parameter is always maximum
|
||||
* amount of cargo to be moved. Second parameter is destination (if
|
||||
* applicable), return value is amount of cargo actually moved. */
|
||||
|
||||
uint Reassign(uint max_move, MoveToAction from, MoveToAction to);
|
||||
uint Return(uint max_move, StationCargoList *dest);
|
||||
uint Unload(uint max_move, StationCargoList *dest, CargoPayment *payment);
|
||||
uint Shift(uint max_move, VehicleCargoList *dest);
|
||||
|
||||
/**
|
||||
* Are two the two CargoPackets mergeable in the context of
|
||||
* a list of CargoPackets for a Vehicle?
|
||||
|
@ -349,6 +426,12 @@ public:
|
|||
* CargoList that is used for stations.
|
||||
*/
|
||||
class StationCargoList : public CargoList<StationCargoList> {
|
||||
protected:
|
||||
/** The (direct) parent of this class. */
|
||||
typedef CargoList<StationCargoList> Parent;
|
||||
|
||||
uint reserved_count; ///< Amount of cargo being reserved for loading.
|
||||
|
||||
public:
|
||||
/** The super class ought to know what it's doing. */
|
||||
friend class CargoList<StationCargoList>;
|
||||
|
@ -359,6 +442,17 @@ public:
|
|||
friend class CargoTransfer;
|
||||
template<class Tsource>
|
||||
friend class CargoRemoval;
|
||||
friend class CargoReservation;
|
||||
friend class CargoReturn;
|
||||
|
||||
void Append(CargoPacket *cp);
|
||||
|
||||
/* Methods for moving cargo around. First parameter is always maximum
|
||||
* amount of cargo to be moved. Second parameter is destination (if
|
||||
* applicable), return value is amount of cargo actually moved. */
|
||||
|
||||
uint Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place);
|
||||
uint Load(uint max_move, VehicleCargoList *dest, TileIndex load_place);
|
||||
|
||||
/**
|
||||
* Are two the two CargoPackets mergeable in the context of
|
||||
|
|
335
src/economy.cpp
335
src/economy.cpp
|
@ -1217,6 +1217,9 @@ void PrepareUnload(Vehicle *front_v)
|
|||
if ((front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
|
||||
for (Vehicle *v = front_v; v != NULL; v = v->Next()) {
|
||||
if (v->cargo_cap > 0 && !v->cargo.Empty()) {
|
||||
v->cargo.Stage(
|
||||
HasBit(Station::Get(front_v->last_station_visited)->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE),
|
||||
front_v->last_station_visited, front_v->current_order.GetUnloadType());
|
||||
SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
|
||||
}
|
||||
}
|
||||
|
@ -1230,6 +1233,86 @@ void PrepareUnload(Vehicle *front_v)
|
|||
front_v->cargo_payment = new CargoPayment(front_v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of cargo the given vehicle can load in the current tick.
|
||||
* This is only about loading speed. The free capacity is ignored.
|
||||
* @param v Vehicle to be queried.
|
||||
* @return Amount of cargo the vehicle can load at once.
|
||||
*/
|
||||
static byte GetLoadAmount(Vehicle *v)
|
||||
{
|
||||
const Engine *e = v->GetEngine();
|
||||
byte load_amount = e->info.load_amount;
|
||||
|
||||
/* The default loadamount for mail is 1/4 of the load amount for passengers */
|
||||
if (v->type == VEH_AIRCRAFT && !Aircraft::From(v)->IsNormalAircraft()) load_amount = CeilDiv(load_amount, 4);
|
||||
|
||||
if (_settings_game.order.gradual_loading) {
|
||||
uint16 cb_load_amount = CALLBACK_FAILED;
|
||||
if (e->GetGRF() != NULL && e->GetGRF()->grf_version >= 8) {
|
||||
/* Use callback 36 */
|
||||
cb_load_amount = GetVehicleProperty(v, PROP_VEHICLE_LOAD_AMOUNT, CALLBACK_FAILED);
|
||||
} else if (HasBit(e->info.callback_mask, CBM_VEHICLE_LOAD_AMOUNT)) {
|
||||
/* Use callback 12 */
|
||||
cb_load_amount = GetVehicleCallback(CBID_VEHICLE_LOAD_AMOUNT, 0, 0, v->engine_type, v);
|
||||
}
|
||||
if (cb_load_amount != CALLBACK_FAILED) {
|
||||
if (e->GetGRF()->grf_version < 8) cb_load_amount = GB(cb_load_amount, 0, 8);
|
||||
if (cb_load_amount >= 0x100) {
|
||||
ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_LOAD_AMOUNT, cb_load_amount);
|
||||
} else if (cb_load_amount != 0) {
|
||||
load_amount = cb_load_amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return load_amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserves cargo if the full load order and improved_load is set or if the
|
||||
* current order allows autorefit.
|
||||
* @param st Station where the consist is loading at the moment.
|
||||
* @param u Front of the loading vehicle consist.
|
||||
* @param consist_capleft If given, save free capacities after reserving there.
|
||||
*/
|
||||
static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft)
|
||||
{
|
||||
Vehicle *next_cargo = u;
|
||||
uint32 seen_cargos = 0;
|
||||
|
||||
while (next_cargo != NULL) {
|
||||
if (next_cargo->cargo_cap == 0) {
|
||||
/* No need to reserve for vehicles without capacity. */
|
||||
next_cargo = next_cargo->Next();
|
||||
continue;
|
||||
}
|
||||
|
||||
CargoID current_cargo = next_cargo->cargo_type;
|
||||
|
||||
Vehicle *v = next_cargo;
|
||||
SetBit(seen_cargos, current_cargo);
|
||||
next_cargo = NULL;
|
||||
for (; v != NULL; v = v->Next()) {
|
||||
if (v->cargo_type != current_cargo) {
|
||||
/* Save start point for next cargo type. */
|
||||
if (next_cargo == NULL && !HasBit(seen_cargos, v->cargo_type)) next_cargo = v;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint cap = v->cargo_cap - v->cargo.RemainingCount();
|
||||
|
||||
/* Nothing to do if the vehicle is full */
|
||||
if (cap > 0) {
|
||||
cap -= st->goods[v->cargo_type].cargo.Reserve(cap, &v->cargo, st->xy);
|
||||
}
|
||||
|
||||
if (consist_capleft != NULL) {
|
||||
(*consist_capleft)[current_cargo] += cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an articulated vehicle is empty.
|
||||
* @param v Vehicle
|
||||
|
@ -1249,31 +1332,24 @@ static bool IsArticulatedVehicleEmpty(Vehicle *v)
|
|||
/**
|
||||
* Loads/unload the vehicle if possible.
|
||||
* @param front the vehicle to be (un)loaded
|
||||
* @param cargo_left the amount of each cargo type that is
|
||||
* virtually left on the platform to be
|
||||
* picked up by another vehicle when all
|
||||
* previous vehicles have loaded.
|
||||
*/
|
||||
static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
||||
static void LoadUnloadVehicle(Vehicle *front)
|
||||
{
|
||||
assert(front->current_order.IsType(OT_LOADING));
|
||||
|
||||
/* We have not waited enough time till the next round of loading/unloading */
|
||||
if (front->load_unload_ticks != 0) {
|
||||
if (_settings_game.order.improved_load && (front->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
|
||||
/* 'Reserve' this cargo for this vehicle, because we were first. */
|
||||
for (Vehicle *v = front; v != NULL; v = v->Next()) {
|
||||
int cap_left = v->cargo_cap;
|
||||
if (!HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) cap_left -= v->cargo.Count();
|
||||
if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
StationID last_visited = front->last_station_visited;
|
||||
Station *st = Station::Get(last_visited);
|
||||
|
||||
bool use_autorefit = front->current_order.IsRefit() && front->current_order.GetRefitCargo() == CT_AUTO_REFIT;
|
||||
CargoArray consist_capleft;
|
||||
if (_settings_game.order.improved_load &&
|
||||
((front->current_order.GetLoadType() & OLFB_FULL_LOAD) != 0 || use_autorefit)) {
|
||||
ReserveConsist(st, front, (use_autorefit && front->load_unload_ticks != 0) ? &consist_capleft : NULL);
|
||||
}
|
||||
|
||||
/* We have not waited enough time till the next round of loading/unloading */
|
||||
if (front->load_unload_ticks != 0) return;
|
||||
|
||||
if (front->type == VEH_TRAIN && (!IsTileType(front->tile, MP_STATION) || GetStationIndex(front->tile) != st->index)) {
|
||||
/* The train reversed in the station. Take the "easy" way
|
||||
* out and let the train just leave as it always did. */
|
||||
|
@ -1282,16 +1358,6 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
return;
|
||||
}
|
||||
|
||||
bool use_autorefit = front->current_order.IsRefit() && front->current_order.GetRefitCargo() == CT_AUTO_REFIT;
|
||||
CargoArray consist_capleft;
|
||||
if (use_autorefit) {
|
||||
/* Sum cargo, that can be loaded without refitting */
|
||||
for (Vehicle *v = front; v != NULL; v = v->Next()) {
|
||||
int cap_left = v->cargo_cap - v->cargo.Count();
|
||||
if (cap_left > 0) consist_capleft[v->cargo_type] += cap_left;
|
||||
}
|
||||
}
|
||||
|
||||
int unloading_time = 0;
|
||||
bool dirty_vehicle = false;
|
||||
bool dirty_station = false;
|
||||
|
@ -1314,81 +1380,49 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
if (v->cargo_cap == 0) continue;
|
||||
artic_part++;
|
||||
|
||||
const Engine *e = v->GetEngine();
|
||||
byte load_amount = e->info.load_amount;
|
||||
|
||||
/* The default loadamount for mail is 1/4 of the load amount for passengers */
|
||||
if (v->type == VEH_AIRCRAFT && !Aircraft::From(v)->IsNormalAircraft()) load_amount = CeilDiv(load_amount, 4);
|
||||
|
||||
if (_settings_game.order.gradual_loading) {
|
||||
uint16 cb_load_amount = CALLBACK_FAILED;
|
||||
if (e->GetGRF() != NULL && e->GetGRF()->grf_version >= 8) {
|
||||
/* Use callback 36 */
|
||||
cb_load_amount = GetVehicleProperty(v, PROP_VEHICLE_LOAD_AMOUNT, CALLBACK_FAILED);
|
||||
} else if (HasBit(e->info.callback_mask, CBM_VEHICLE_LOAD_AMOUNT)) {
|
||||
/* Use callback 12 */
|
||||
cb_load_amount = GetVehicleCallback(CBID_VEHICLE_LOAD_AMOUNT, 0, 0, v->engine_type, v);
|
||||
}
|
||||
if (cb_load_amount != CALLBACK_FAILED) {
|
||||
if (e->GetGRF()->grf_version < 8) cb_load_amount = GB(cb_load_amount, 0, 8);
|
||||
if (cb_load_amount >= 0x100) {
|
||||
ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_LOAD_AMOUNT, cb_load_amount);
|
||||
} else if (cb_load_amount != 0) {
|
||||
load_amount = cb_load_amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
byte load_amount = GetLoadAmount(v);
|
||||
|
||||
GoodsEntry *ge = &st->goods[v->cargo_type];
|
||||
|
||||
if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && (front->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
|
||||
uint cargo_count = v->cargo.Count();
|
||||
uint cargo_count = v->cargo.UnloadCount();
|
||||
uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
|
||||
bool remaining = false; // Are there cargo entities in this vehicle that can still be unloaded here?
|
||||
bool accepted = false; // Is the cargo accepted by the station?
|
||||
|
||||
payment->SetCargo(v->cargo_type);
|
||||
|
||||
if (HasBit(ge->acceptance_pickup, GoodsEntry::GES_ACCEPTANCE) && !(front->current_order.GetUnloadType() & OUFB_TRANSFER)) {
|
||||
/* The cargo has reached its final destination, the packets may now be destroyed */
|
||||
remaining = v->cargo.MoveTo<StationCargoList>(NULL, amount_unloaded, VehicleCargoList::MTA_FINAL_DELIVERY, payment, last_visited);
|
||||
if (!HasBit(ge->acceptance_pickup, GoodsEntry::GES_ACCEPTANCE) && v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER) > 0) {
|
||||
/* The station does not accept our goods anymore. */
|
||||
if (front->current_order.GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) {
|
||||
/* Transfer instead of delivering. */
|
||||
v->cargo.Reassign(v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER),
|
||||
VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_TRANSFER);
|
||||
} else {
|
||||
/* Keep instead of delivering. This may lead to no cargo being unloaded, so ...*/
|
||||
v->cargo.Reassign(v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER),
|
||||
VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_KEEP);
|
||||
|
||||
dirty_vehicle = true;
|
||||
accepted = true;
|
||||
}
|
||||
|
||||
/* The !accepted || v->cargo.Count == cargo_count clause is there
|
||||
* to make it possible to force unload vehicles at the station where
|
||||
* they were loaded, but to not force unload the vehicle when the
|
||||
* station is still accepting the cargo in the vehicle. It doesn't
|
||||
* accept cargo that was loaded at the same station. */
|
||||
if ((front->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) && (!accepted || v->cargo.Count() == cargo_count)) {
|
||||
remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded, front->current_order.GetUnloadType() & OUFB_TRANSFER ? VehicleCargoList::MTA_TRANSFER : VehicleCargoList::MTA_UNLOAD, payment);
|
||||
if (!HasBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP)) {
|
||||
SetBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP);
|
||||
InvalidateWindowData(WC_STATION_LIST, last_visited);
|
||||
/* ... say we unloaded something, otherwise we'll think we didn't unload
|
||||
* something and we didn't load something, so we must be finished
|
||||
* at this station. Setting the unloaded means that we will get a
|
||||
* retry for loading in the next cycle. */
|
||||
anything_unloaded = true;
|
||||
}
|
||||
|
||||
dirty_vehicle = dirty_station = true;
|
||||
} else if (!accepted) {
|
||||
/* The order changed while unloading (unset unload/transfer) or the
|
||||
* station does not accept our goods. */
|
||||
ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
|
||||
|
||||
/* Say we loaded something, otherwise we'll think we didn't unload
|
||||
* something and we didn't load something, so we must be finished
|
||||
* at this station. Setting the unloaded means that we will get a
|
||||
* retry for loading in the next cycle. */
|
||||
anything_unloaded = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Deliver goods to the station */
|
||||
st->time_since_unload = 0;
|
||||
/* Mark the station dirty if we transfer, but not if we only deliver. */
|
||||
dirty_station = v->cargo.ActionCount(VehicleCargoList::MTA_TRANSFER) > 0;
|
||||
amount_unloaded = v->cargo.Unload(amount_unloaded, &ge->cargo, payment);
|
||||
remaining = v->cargo.UnloadCount() > 0;
|
||||
if (amount_unloaded > 0) {
|
||||
dirty_vehicle = true;
|
||||
anything_unloaded = true;
|
||||
unloading_time += amount_unloaded;
|
||||
|
||||
unloading_time += amount_unloaded;
|
||||
/* Deliver goods to the station */
|
||||
st->time_since_unload = 0;
|
||||
}
|
||||
|
||||
anything_unloaded = true;
|
||||
if (_settings_game.order.gradual_loading && remaining) {
|
||||
completely_emptied = false;
|
||||
} else {
|
||||
|
@ -1419,7 +1453,7 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
Backup<CompanyByte> cur_company(_current_company, front->owner, FILE_LINE);
|
||||
|
||||
/* Check if all articulated parts are empty and collect refit mask. */
|
||||
uint32 refit_mask = e->info.refit_mask;
|
||||
uint32 refit_mask = v->GetEngine()->info.refit_mask;
|
||||
Vehicle *w = v_start;
|
||||
while (w->HasArticulatedPart()) {
|
||||
w = w->GetNextArticulatedPart();
|
||||
|
@ -1434,13 +1468,13 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
FOR_EACH_SET_CARGO_ID(cid, refit_mask) {
|
||||
/* Consider refitting to this cargo, if other vehicles of the consist cannot
|
||||
* already take the cargo without refitting */
|
||||
if (cargo_left[cid] > (int)consist_capleft[cid] + amount) {
|
||||
if ((int)st->goods[cid].cargo.Count() > (int)consist_capleft[cid] + amount) {
|
||||
/* Try to find out if auto-refitting would succeed. In case the refit is allowed,
|
||||
* the returned refit capacity will be greater than zero. */
|
||||
new_subtype = GetBestFittingSubType(v, v, cid);
|
||||
DoCommand(v_start->tile, v_start->index, cid | 1U << 6 | new_subtype << 8 | 1U << 16, DC_QUERY_COST, GetCmdRefitVeh(v_start)); // Auto-refit and only this vehicle including artic parts.
|
||||
if (_returned_refit_capacity > 0) {
|
||||
amount = cargo_left[cid] - consist_capleft[cid];
|
||||
amount = st->goods[cid].cargo.Count() - consist_capleft[cid];
|
||||
new_cid = cid;
|
||||
}
|
||||
}
|
||||
|
@ -1454,12 +1488,13 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
ge = &st->goods[v->cargo_type];
|
||||
}
|
||||
|
||||
/* Add new capacity to consist capacity */
|
||||
consist_capleft[v_start->cargo_type] += v_start->cargo_cap;
|
||||
for (Vehicle *w = v_start; w->HasArticulatedPart(); ) {
|
||||
w = w->GetNextArticulatedPart();
|
||||
consist_capleft[w->cargo_type] += w->cargo_cap;
|
||||
}
|
||||
/* Add new capacity to consist capacity and reserve cargo */
|
||||
w = v_start;
|
||||
do {
|
||||
st->goods[w->cargo_type].cargo.Reserve(w->cargo_cap, &w->cargo, st->xy);
|
||||
consist_capleft[w->cargo_type] += w->cargo_cap - w->cargo.Count();
|
||||
w = w->HasArticulatedPart() ? w->GetNextArticulatedPart() : NULL;
|
||||
} while (w != NULL);
|
||||
|
||||
cur_company.Restore();
|
||||
}
|
||||
|
@ -1490,54 +1525,25 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
|
||||
/* If there's goods waiting at the station, and the vehicle
|
||||
* has capacity for it, load it on the vehicle. */
|
||||
int cap_left = v->cargo_cap - v->cargo.Count();
|
||||
if (!ge->cargo.Empty() && cap_left > 0) {
|
||||
uint cap = cap_left;
|
||||
uint count = ge->cargo.Count();
|
||||
int cap_left = v->cargo_cap - v->cargo.OnboardCount();
|
||||
if (cap_left > 0 && (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0 || !ge->cargo.Empty())) {
|
||||
if (_settings_game.order.gradual_loading) cap_left = min(cap_left, load_amount);
|
||||
if (v->cargo.OnboardCount() == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
|
||||
|
||||
/* Skip loading this vehicle if another train/vehicle is already handling
|
||||
* the same cargo type at this station */
|
||||
if (_settings_game.order.improved_load && cargo_left[v->cargo_type] <= 0) {
|
||||
SetBit(cargo_not_full, v->cargo_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cap > count) cap = count;
|
||||
if (_settings_game.order.gradual_loading) {
|
||||
cap = min(cap, load_amount);
|
||||
cap_left = min(cap_left, load_amount);
|
||||
}
|
||||
if (_settings_game.order.improved_load) {
|
||||
/* Don't load stuff that is already 'reserved' for other vehicles */
|
||||
cap = min((uint)cargo_left[v->cargo_type], cap);
|
||||
count = cargo_left[v->cargo_type];
|
||||
if (use_autorefit) {
|
||||
/* When using autorefit, reserve all cargo for this wagon to prevent other wagons
|
||||
* from feeling the need to refit. */
|
||||
uint total_cap_left = v->cargo_cap - v->cargo.Count();
|
||||
cargo_left[v->cargo_type] -= total_cap_left;
|
||||
consist_capleft[v->cargo_type] -= total_cap_left;
|
||||
if (total_cap_left > cap && count > cap) {
|
||||
/* Remember if there are reservations left so that we don't stop
|
||||
* loading before they're loaded. */
|
||||
SetBit(reservation_left, v->cargo_type);
|
||||
}
|
||||
} else {
|
||||
/* Update cargo left; but don't reserve everything yet, so other wagons
|
||||
* of the same consist load in parallel. */
|
||||
cargo_left[v->cargo_type] -= cap;
|
||||
}
|
||||
uint loaded = ge->cargo.Load(cap_left, &v->cargo, st->xy);
|
||||
if (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
|
||||
/* Remember if there are reservations left so that we don't stop
|
||||
* loading before they're loaded. */
|
||||
SetBit(reservation_left, v->cargo_type);
|
||||
}
|
||||
|
||||
/* Store whether the maximum possible load amount was loaded or not.*/
|
||||
if (count >= (uint)cap_left) {
|
||||
if (loaded == (uint)cap_left) {
|
||||
SetBit(full_load_amount, v->cargo_type);
|
||||
} else {
|
||||
ClrBit(full_load_amount, v->cargo_type);
|
||||
}
|
||||
|
||||
if (v->cargo.Empty()) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
|
||||
|
||||
/* TODO: Regarding this, when we do gradual loading, we
|
||||
* should first unload all vehicles and then start
|
||||
* loading them. Since this will cause
|
||||
|
@ -1545,26 +1551,26 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
* the whole vehicle chain is really totally empty, the
|
||||
* completely_emptied assignment can then be safely
|
||||
* removed; that's how TTDPatch behaves too. --pasky */
|
||||
completely_emptied = false;
|
||||
anything_loaded = true;
|
||||
if (loaded > 0) {
|
||||
completely_emptied = false;
|
||||
anything_loaded = true;
|
||||
|
||||
ge->cargo.MoveTo(&v->cargo, cap, StationCargoList::MTA_CARGO_LOAD, NULL, st->xy);
|
||||
st->time_since_load = 0;
|
||||
st->last_vehicle_type = v->type;
|
||||
|
||||
st->time_since_load = 0;
|
||||
st->last_vehicle_type = v->type;
|
||||
if (ge->cargo.Empty()) {
|
||||
TriggerStationRandomisation(st, st->xy, SRT_CARGO_TAKEN, v->cargo_type);
|
||||
TriggerStationAnimation(st, st->xy, SAT_CARGO_TAKEN, v->cargo_type);
|
||||
AirportAnimationTrigger(st, AAT_STATION_CARGO_TAKEN, v->cargo_type);
|
||||
}
|
||||
|
||||
if (ge->cargo.Empty()) {
|
||||
TriggerStationRandomisation(st, st->xy, SRT_CARGO_TAKEN, v->cargo_type);
|
||||
TriggerStationAnimation(st, st->xy, SAT_CARGO_TAKEN, v->cargo_type);
|
||||
AirportAnimationTrigger(st, AAT_STATION_CARGO_TAKEN, v->cargo_type);
|
||||
unloading_time += loaded;
|
||||
|
||||
dirty_vehicle = dirty_station = true;
|
||||
}
|
||||
|
||||
unloading_time += cap;
|
||||
|
||||
dirty_vehicle = dirty_station = true;
|
||||
}
|
||||
|
||||
if (v->cargo.Count() >= v->cargo_cap) {
|
||||
if (v->cargo.OnboardCount() >= v->cargo_cap) {
|
||||
SetBit(cargo_full, v->cargo_type);
|
||||
} else {
|
||||
SetBit(cargo_not_full, v->cargo_type);
|
||||
|
@ -1581,27 +1587,6 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
/* Only set completely_emptied, if we just unloaded all remaining cargo */
|
||||
completely_emptied &= anything_unloaded;
|
||||
|
||||
/* For consists without autorefit-order we adjust the reserved cargo at the station after loading,
|
||||
* so that all wagons start loading if the consist is the first consist.
|
||||
*
|
||||
* If we use autorefit otoh, we only want to load/refit a vehicle if the other wagons cannot already hold the cargo,
|
||||
* to keep the option to still refit the vehicle when new cargo of different type shows up.
|
||||
*/
|
||||
if (_settings_game.order.improved_load && (front->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
|
||||
/* Update left cargo */
|
||||
for (Vehicle *v = front; v != NULL; v = v->Next()) {
|
||||
int cap_left = v->cargo_cap;
|
||||
if (!HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) {
|
||||
if (use_autorefit) {
|
||||
continue;
|
||||
} else {
|
||||
cap_left -= v->cargo.Count();
|
||||
}
|
||||
}
|
||||
if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
|
||||
}
|
||||
}
|
||||
|
||||
if (!anything_unloaded) delete payment;
|
||||
|
||||
ClrBit(front->vehicle_flags, VF_STOP_LOADING);
|
||||
|
@ -1625,7 +1610,7 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left)
|
|||
if (front->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
|
||||
/* if the aircraft carries passengers and is NOT full, then
|
||||
* continue loading, no matter how much mail is in */
|
||||
if ((front->type == VEH_AIRCRAFT && IsCargoInClass(front->cargo_type, CC_PASSENGERS) && front->cargo_cap > front->cargo.Count()) ||
|
||||
if ((front->type == VEH_AIRCRAFT && IsCargoInClass(front->cargo_type, CC_PASSENGERS) && front->cargo_cap > front->cargo.OnboardCount()) ||
|
||||
(cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) { // There are still non-full cargoes
|
||||
finished_loading = false;
|
||||
}
|
||||
|
@ -1716,13 +1701,9 @@ void LoadUnloadStation(Station *st)
|
|||
*/
|
||||
if (last_loading == NULL) return;
|
||||
|
||||
int cargo_left[NUM_CARGO];
|
||||
|
||||
for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count();
|
||||
|
||||
for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
|
||||
Vehicle *v = *iter;
|
||||
if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left);
|
||||
if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v);
|
||||
if (v == last_loading) break;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,11 @@
|
|||
for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsSavegameVersionBefore(181)) {
|
||||
Vehicle *v;
|
||||
FOR_ALL_VEHICLES(v) v->cargo.KeepAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -244,8 +244,9 @@
|
|||
* 178 24789
|
||||
* 179 24810
|
||||
* 180 24998
|
||||
* 181 25012
|
||||
*/
|
||||
extern const uint16 SAVEGAME_VERSION = 180; ///< Current savegame version of OpenTTD.
|
||||
extern const uint16 SAVEGAME_VERSION = 181; ///< Current savegame version of OpenTTD.
|
||||
|
||||
SavegameType _savegame_type; ///< type of savegame we are loading
|
||||
|
||||
|
|
|
@ -259,7 +259,7 @@ const SaveLoad *GetGoodsDesc()
|
|||
SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67),
|
||||
SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, 150, SL_MAX_VERSION),
|
||||
SLE_CONDLST(GoodsEntry, cargo.packets, REF_CARGO_PACKET, 68, SL_MAX_VERSION),
|
||||
|
||||
SLE_CONDVAR(GoodsEntry, cargo.reserved_count,SLE_UINT, 181, SL_MAX_VERSION),
|
||||
SLE_END()
|
||||
};
|
||||
|
||||
|
|
|
@ -614,6 +614,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
|
|||
SLE_VAR(Vehicle, cargo_cap, SLE_UINT16),
|
||||
SLEG_CONDVAR( _cargo_count, SLE_UINT16, 0, 67),
|
||||
SLE_CONDLST(Vehicle, cargo.packets, REF_CARGO_PACKET, 68, SL_MAX_VERSION),
|
||||
SLE_CONDARR(Vehicle, cargo.action_counts, SLE_UINT, VehicleCargoList::NUM_MOVE_TO_ACTION, 181, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Vehicle, cargo_age_counter, SLE_UINT16, 162, SL_MAX_VERSION),
|
||||
|
||||
SLE_VAR(Vehicle, day_counter, SLE_UINT8),
|
||||
|
|
|
@ -720,10 +720,11 @@ void Vehicle::PreDestructor()
|
|||
if (CleaningPool()) return;
|
||||
|
||||
if (Station::IsValidID(this->last_station_visited)) {
|
||||
Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
|
||||
Station *st = Station::Get(this->last_station_visited);
|
||||
st->loading_vehicles.remove(this);
|
||||
|
||||
HideFillingPercent(&this->fill_percent_te_id);
|
||||
|
||||
this->CancelReservation(st);
|
||||
delete this->cargo_payment;
|
||||
}
|
||||
|
||||
|
@ -1255,7 +1256,7 @@ uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
|
|||
|
||||
/* Count up max and used */
|
||||
for (const Vehicle *v = front; v != NULL; v = v->Next()) {
|
||||
count += v->cargo.Count();
|
||||
count += v->cargo.OnboardCount();
|
||||
max += v->cargo_cap;
|
||||
if (v->cargo_cap != 0 && colour != NULL) {
|
||||
unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
|
||||
|
@ -1980,6 +1981,22 @@ void Vehicle::BeginLoading()
|
|||
this->MarkDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all reserved cargo packets to the station.
|
||||
* @param st the station where the reserved packets should go.
|
||||
*/
|
||||
void Vehicle::CancelReservation(Station *st)
|
||||
{
|
||||
for (Vehicle *v = this; v != NULL; v = v->next) {
|
||||
VehicleCargoList &cargo = v->cargo;
|
||||
if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
|
||||
DEBUG(misc, 1, "cancelling cargo reservation");
|
||||
cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo);
|
||||
cargo.KeepAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform all actions when leaving a station.
|
||||
* @pre this->current_order.IsType(OT_LOADING)
|
||||
|
@ -1995,6 +2012,7 @@ void Vehicle::LeaveStation()
|
|||
|
||||
this->current_order.MakeLeaveStation();
|
||||
Station *st = Station::Get(this->last_station_visited);
|
||||
this->CancelReservation(st);
|
||||
st->loading_vehicles.remove(this);
|
||||
|
||||
HideFillingPercent(&this->fill_percent_te_id);
|
||||
|
|
|
@ -244,6 +244,7 @@ public:
|
|||
virtual ~Vehicle();
|
||||
|
||||
void BeginLoading();
|
||||
void CancelReservation(Station *st);
|
||||
void LeaveStation();
|
||||
|
||||
GroundVehicleCache *GetGroundVehicleCache();
|
||||
|
|
Loading…
Reference in New Issue