diff options
Diffstat (limited to 'src/cargopacket.cpp')
-rw-r--r-- | src/cargopacket.cpp | 314 |
1 files changed, 291 insertions, 23 deletions
diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index 0b92de0c8..42eca0b55 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -12,6 +12,10 @@ #include "stdafx.h" #include "core/pool_func.hpp" #include "economy_base.h" +#include "station_base.h" +#include "cargodest_func.h" +#include "cargodest_base.h" +#include "settings_type.h" /* Initialize the cargopacket-pool */ CargoPacketPool _cargopacket_pool("CargoPacket"); @@ -24,6 +28,12 @@ CargoPacket::CargoPacket() { this->source_type = ST_INDUSTRY; this->source_id = INVALID_SOURCE; + this->dest_xy = INVALID_TILE; + this->dest_id = INVALID_SOURCE; + this->dest_type = ST_INDUSTRY; + this->flags = 0; + this->next_order = INVALID_ORDER; + this->next_station = INVALID_STATION; } /** @@ -33,21 +43,33 @@ CargoPacket::CargoPacket() * @param count Number of cargo entities to put in this packet. * @param source_type 'Type' of source the packet comes from (for subsidies). * @param source_id Actual source of the packet (for subsidies). + * @param dest_xy Destination location of the packet. + * @param dest_type 'Type' of the destination. + * @param dest_id Actual destination of the packet. + * @param next_order Desired next hop of the packet. + * @param next_station Station to unload the packet next. + * @param flags Routing flags of the packet. * @pre count != 0 * @note We have to zero memory ourselves here because we are using a 'new' * that, in contrary to all other pools, does not memset to 0. */ -CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id) : +CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id, TileIndex dest_xy, SourceType dest_type, SourceID dest_id, OrderID next_order, StationID next_station, byte flags) : feeder_share(0), count(count), days_in_transit(0), source_id(source_id), source(source), source_xy(source_xy), - loaded_at_xy(0) + loaded_at_xy(0), + dest_xy(dest_xy), + dest_id(dest_id), + flags(flags), + next_order(next_order), + next_station(next_station) { assert(count != 0); this->source_type = source_type; + this->dest_type = dest_type; } /** @@ -61,20 +83,32 @@ CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, So * @param feeder_share Feeder share the packet has already accumulated. * @param source_type 'Type' of source the packet comes from (for subsidies). * @param source_id Actual source of the packet (for subsidies). + * @param dest_xy Destination location of the packet. + * @param dest_type 'Type' of the destination. + * @param dest_id Actual destination of the packet. + * @param next_order Desired next hop of the packet. + * @param next_station Station to unload the packet next. + * @param flags Routing flags of the packet. * @note We have to zero memory ourselves here because we are using a 'new' * that, in contrary to all other pools, does not memset to 0. */ -CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, TileIndex source_xy, TileIndex loaded_at_xy, Money feeder_share, SourceType source_type, SourceID source_id) : +CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, TileIndex source_xy, TileIndex loaded_at_xy, Money feeder_share, SourceType source_type, SourceID source_id, TileIndex dest_xy, SourceType dest_type, SourceID dest_id, OrderID next_order, StationID next_station, byte flags) : feeder_share(feeder_share), count(count), days_in_transit(days_in_transit), source_id(source_id), source(source), source_xy(source_xy), - loaded_at_xy(loaded_at_xy) + loaded_at_xy(loaded_at_xy), + dest_xy(dest_xy), + dest_id(dest_id), + flags(flags), + next_order(next_order), + next_station(next_station) { assert(count != 0); this->source_type = source_type; + this->dest_type = dest_type; } /** @@ -87,7 +121,7 @@ inline CargoPacket *CargoPacket::Split(uint new_size) if (!CargoPacket::CanAllocateItem()) return NULL; Money fs = this->feeder_share * new_size / static_cast<uint>(this->count); - CargoPacket *cp_new = new CargoPacket(new_size, this->days_in_transit, this->source, this->source_xy, this->loaded_at_xy, fs, this->source_type, this->source_id); + CargoPacket *cp_new = new CargoPacket(new_size, this->days_in_transit, this->source, this->source_xy, this->loaded_at_xy, fs, this->source_type, this->source_id, this->dest_xy, this->dest_type, this->dest_id, this->next_order, this->next_station, this->flags); this->feeder_share -= fs; this->count -= new_size; return cp_new; @@ -111,9 +145,16 @@ inline void CargoPacket::Merge(CargoPacket *cp) */ /* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src) { + /* Clear next hop of those packets that loose their destination. */ + StationCargoList::InvalidateAllTo(src_type, src); + CargoPacket *cp; FOR_ALL_CARGOPACKETS(cp) { if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE; + if (cp->dest_type == src_type && cp->dest_id == src) { + cp->dest_id = INVALID_SOURCE; + cp->dest_xy = INVALID_TILE; + } } } @@ -126,6 +167,7 @@ inline void CargoPacket::Merge(CargoPacket *cp) CargoPacket *cp; FOR_ALL_CARGOPACKETS(cp) { if (cp->source == sid) cp->source = INVALID_STATION; + if (cp->next_station == sid) cp->next_station = INVALID_STATION; } } @@ -229,6 +271,7 @@ void CargoList<Tinst>::Truncate(uint max_remaining) uint diff = local_count - max_remaining; this->count -= diff; this->cargo_days_in_transit -= cp->days_in_transit * diff; + static_cast<Tinst *>(this)->RemoveFromCacheLocal(cp, diff); cp->count = max_remaining; max_remaining = 0; } else { @@ -245,56 +288,121 @@ void CargoList<Tinst>::Truncate(uint max_remaining) * - 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. + * - MTA_NO_ACTION: Does nothing for packets without destination, otherwise either like MTA_TRANSFER or MTA_FINAL_DELIVERY. * @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 st Station ID where we are loading/unloading or STATION_INVALID for move from vehicle to vehicle. * @param payment The payment helper. + * @param cur_order The current order of the loading vehicle. + * @param did_transfer Set to true if some cargo was transfered. * * @pre mta == MTA_FINAL_DELIVERY || dest != NULL * @pre mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL + * @pre st != INVALID_STATION || (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) +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; - if (cp->source == data && mta == MTA_FINAL_DELIVERY) { - /* Skip cargo that originated from this station. */ + 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); - switch (mta) { + 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 = data; + cp->loaded_at_xy = Station::Get(st)->xy; break; case MTA_TRANSFER: cp->feeder_share += payment->PayTransfer(cp, cp->count); break; - case MTA_UNLOAD: + default: break; } dest->Append(cp); @@ -302,7 +410,7 @@ bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta } /* Can move only part of the packet */ - if (mta == MTA_FINAL_DELIVERY) { + if (cp_mta == MTA_FINAL_DELIVERY) { /* Final delivery doesn't need package splitting. */ payment->PayFinalDelivery(cp, max_move); @@ -323,12 +431,14 @@ bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta 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 (mta == MTA_TRANSFER) { + 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 (mta == MTA_CARGO_LOAD) { - cp_new->loaded_at_xy = data; + } else if (cp_mta == MTA_CARGO_LOAD) { + cp_new->loaded_at_xy = Station::Get(st)->xy; } dest->Append(cp_new); @@ -337,6 +447,12 @@ bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta 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(); } @@ -396,6 +512,158 @@ void VehicleCargoList::InvalidateCache() this->Parent::InvalidateCache(); } +/** Invalidate next unload station of all cargo packets. */ +void VehicleCargoList::InvalidateNextStation() +{ + for (VehicleCargoList::ConstIterator it = this->packets.begin(); it != this->packets.end(); ++it) { + (*it)->next_station = INVALID_STATION; + } +} + +/** + * Update the local next-hop count cache. + * @param cp Packet the be removed. + * @param amount Cargo amount to be removed. + */ +void StationCargoList::RemoveFromCacheLocal(const CargoPacket *cp, uint amount) +{ + this->order_cache[cp->next_order] -= amount; + if (this->order_cache[cp->next_order] == 0) this->order_cache.erase(cp->next_order); +} + +/** + * Update the cached values to reflect the removal of this packet. + * Decreases count and days_in_transit. + * @param cp Packet to be removed from cache. + */ +void StationCargoList::RemoveFromCache(const CargoPacket *cp) +{ + this->RemoveFromCacheLocal(cp, cp->count); + this->Parent::RemoveFromCache(cp); +} + +/** + * Update the cache to reflect adding of this packet. + * Increases count and days_in_transit. + * @param cp New packet to be inserted. + */ +void StationCargoList::AddToCache(const CargoPacket *cp) +{ + this->order_cache[cp->next_order] += cp->count; + this->Parent::AddToCache(cp); +} + +/** Invalidates the cached data and rebuild it. */ +void StationCargoList::InvalidateCache() +{ + this->order_cache.clear(); + this->Parent::InvalidateCache(); +} + +/** + * Recompute the desired next hop of a cargo packet. + * @param cp Cargo packet to update. + * @param st Station of this list. + * @param cid Cargo type of this list. + * @return False if the packet was deleted, true otherwise. + */ +bool StationCargoList::UpdateCargoNextHop(CargoPacket *cp, Station *st, CargoID cid) +{ + StationID next_unload; + RouteLink *l = FindRouteLinkForCargo(st, cid, cp, &next_unload); + + if (l == NULL) { + /* No link to destination, drop packet. */ + this->RemoveFromCache(cp); + delete cp; + return false; + } + + /* Update next hop info. */ + this->RemoveFromCache(cp); + cp->next_station = next_unload; + cp->next_order = l->GetOriginOrderId(); + this->AddToCache(cp); + + return true; +} + +/** + * Recompute the desired next hop of all cargo packets. + * @param st Station of this list. + * @param cid Cargo type of this list. + */ +void StationCargoList::UpdateCargoNextHop(Station *st, CargoID cid) +{ + uint count = 0; + StationCargoList::Iterator iter; + for (iter = this->packets.begin(); count < this->next_start + _settings_game.economy.cargodest.route_recalc_chunk && iter != this->packets.end(); count++) { + if (count < this->next_start) continue; + if ((*iter)->DestinationID() != INVALID_SOURCE) { + if (this->UpdateCargoNextHop(*iter, st, cid)) { + ++iter; + } else { + iter = this->packets.erase(iter); + } + } else { + ++iter; + } + } + + /* Update start counter for next loop. */ + this->next_start = count; + if (this->next_start >= this->packets.size()) this->next_start = 0; +} + +/** + * Invalidates the next hop info of all cargo packets with a given next order or unload station. + * @param order Next order to invalidate. + * @param st_unload Unload station to invalidate. + */ +/* static */ void StationCargoList::InvalidateAllTo(OrderID order, StationID st_unload) +{ + Station *st; + FOR_ALL_STATIONS(st) { + for (CargoID cid = 0; cid < NUM_CARGO; cid++) { + for (StationCargoList::Iterator it = st->goods[cid].cargo.packets.begin(); it != st->goods[cid].cargo.packets.end(); ++it) { + CargoPacket *cp = *it; + if (cp->next_order == order || cp->next_station == st_unload) { + /* Invalidate both order and unload station as both likely + * don't make sense anymore. */ + st->goods[cid].cargo.RemoveFromCache(cp); + cp->next_order = INVALID_ORDER; + cp->next_station = INVALID_STATION; + st->goods[cid].cargo.AddToCache(cp); + } + } + } + } +} + +/** + * Invalidates the next hop info of all cargo packets for a given destination. + * @param order Next order to invalidate. + */ +/* static */ void StationCargoList::InvalidateAllTo(SourceType type, SourceID dest) +{ + Station *st; + FOR_ALL_STATIONS(st) { + for (CargoID cid = 0; cid < NUM_CARGO; cid++) { + for (StationCargoList::Iterator it = st->goods[cid].cargo.packets.begin(); it != st->goods[cid].cargo.packets.end(); ++it) { + CargoPacket *cp = *it; + if (cp->dest_id == dest && cp->dest_type == type) { + /* Invalidate both next order and unload station as we + * want the packets to be not routed anymore. */ + st->goods[cid].cargo.RemoveFromCache(cp); + cp->next_order = INVALID_ORDER; + cp->next_station = INVALID_STATION; + st->goods[cid].cargo.AddToCache(cp); + } + } + } + } +} + /* * We have to instantiate everything we want to be usable. */ @@ -403,8 +671,8 @@ 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); +template bool CargoList<VehicleCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer); /** Cargo unloading at a station. */ -template bool CargoList<VehicleCargoList>::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data); +template bool CargoList<VehicleCargoList>::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer); /** Cargo loading at a station. */ -template bool CargoList<StationCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data); +template bool CargoList<StationCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer); |