diff --git projects/openttd_vs80.vcproj projects/openttd_vs80.vcproj
index 83ef5af..47d5f9f 100644
--- projects/openttd_vs80.vcproj
+++ projects/openttd_vs80.vcproj
@@ -588,6 +588,22 @@
>
+
+
+
+
+
+
+
+
@@ -1084,6 +1100,30 @@
>
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1100,6 +1140,14 @@
>
+
+
+
+
@@ -1700,6 +1748,10 @@
>
+
+
@@ -2116,6 +2168,10 @@
>
+
+
diff --git projects/openttd_vs90.vcproj projects/openttd_vs90.vcproj
index 80e9a3f..78940a7 100644
--- projects/openttd_vs90.vcproj
+++ projects/openttd_vs90.vcproj
@@ -585,6 +585,22 @@
>
+
+
+
+
+
+
+
+
@@ -1081,6 +1097,30 @@
>
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1097,6 +1137,14 @@
>
+
+
+
+
@@ -1697,6 +1745,10 @@
>
+
+
@@ -2113,6 +2165,10 @@
>
+
+
diff --git source.list source.list
index 23c61bd..4129fe0 100644
--- source.list
+++ source.list
@@ -32,6 +32,10 @@ heightmap.cpp
highscore.cpp
ini.cpp
landscape.cpp
+linkgraph/demands.cpp
+linkgraph/flowmapper.cpp
+linkgraph/linkgraph.cpp
+linkgraph/mcf.cpp
map.cpp
misc.cpp
mixer.cpp
@@ -180,10 +184,18 @@ industrytype.h
ini_type.h
landscape.h
landscape_type.h
+linkgraph/demands.h
+linkgraph/demand_settings.h
+linkgraph/flowmapper.h
+linkgraph/linkgraph.h
+linkgraph/linkgraph_type.h
+linkgraph/mcf.h
livery.h
map_func.h
map_type.h
mixer.h
+moving_average.h
+moving_average.cpp
network/network.h
network/network_base.h
network/network_client.h
@@ -350,6 +362,7 @@ core/geometry_type.hpp
core/math_func.cpp
core/math_func.hpp
core/mem_func.hpp
+core/multimap.hpp
core/overflowsafe_type.hpp
core/pool_func.hpp
core/pool_type.hpp
@@ -458,6 +471,7 @@ saveload/gamelog_sl.cpp
saveload/group_sl.cpp
saveload/industry_sl.cpp
saveload/labelmaps_sl.cpp
+saveload/linkgraph_sl.cpp
saveload/map_sl.cpp
saveload/misc_sl.cpp
saveload/newgrf_sl.cpp
diff --git src/aircraft_cmd.cpp src/aircraft_cmd.cpp
index e5be7fe..ce3addf 100644
--- src/aircraft_cmd.cpp
+++ src/aircraft_cmd.cpp
@@ -1306,6 +1306,7 @@ static void AircraftEntersTerminal(Aircraft *v)
if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
Station *st = Station::Get(v->targetairport);
+ StationID previous_station = v->last_station_visited;
v->last_station_visited = v->targetairport;
/* Check if station was ever visited before */
@@ -1322,7 +1323,7 @@ static void AircraftEntersTerminal(Aircraft *v)
AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
}
- v->BeginLoading();
+ v->BeginLoading(previous_station);
}
static void AircraftLandAirplane(Aircraft *v)
diff --git src/autoreplace_cmd.cpp src/autoreplace_cmd.cpp
index 26a92e5..334c528 100644
--- src/autoreplace_cmd.cpp
+++ src/autoreplace_cmd.cpp
@@ -118,7 +118,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.MoveTo(&dest->cargo, amount);
}
}
diff --git src/cargo_type.h src/cargo_type.h
index 8d9a1a8..277a01c 100644
--- src/cargo_type.h
+++ src/cargo_type.h
@@ -13,6 +13,8 @@
#define CARGO_TYPE_H
#include "core/enum_type.hpp"
+#include "core/math_func.hpp"
+#include "saveload/saveload.h"
/**
* Cargo slots to indicate a cargo type within a game.
@@ -23,6 +25,7 @@ typedef byte CargoID;
/** Available types of cargo */
enum CargoTypes {
+ CT_BEGIN = 0,
/* Temperate */
CT_PASSENGERS = 0,
CT_COAL = 1,
@@ -64,6 +67,7 @@ enum CargoTypes {
CT_FIZZY_DRINKS = 11,
NUM_CARGO = 32, ///< Maximal number of cargo types in a game.
+ CT_END = 32,
CT_NO_REFIT = 0xFE, ///< Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-new).
CT_INVALID = 0xFF, ///< Invalid cargo type.
@@ -104,6 +108,24 @@ public:
}
};
+class GlobalCargoAcceptance {
+private:
+ CargoArray acceptance;
+ CargoArray current_tile_loop;
+
+public:
+ friend const SaveLoad *GetGlobalCargoAcceptanceDesc();
+
+ FORCEINLINE uint Get(CargoID c) {return (acceptance)[c];}
+ FORCEINLINE CargoArray &CurrentLoop() {return current_tile_loop;}
+ FORCEINLINE void NewLoop()
+ {
+ acceptance = current_tile_loop;
+ current_tile_loop.Clear();
+ }
+
+ static GlobalCargoAcceptance inst;
+};
/** Types of cargo source and destination */
enum SourceType {
diff --git src/cargopacket.cpp src/cargopacket.cpp
index 418eb8e..2bc031e 100644
--- src/cargopacket.cpp
+++ src/cargopacket.cpp
@@ -10,6 +10,7 @@
/** @file cargopacket.cpp Implementation of the cargo packets */
#include "stdafx.h"
+#include "station_base.h"
#include "core/pool_func.hpp"
#include "economy_base.h"
@@ -47,13 +48,13 @@ CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, So
}
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) :
- 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)
+ 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)
{
assert(count != 0);
this->source_type = source_type;
@@ -72,6 +73,22 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
}
}
+CargoPacket * CargoPacket::Split(uint new_size)
+{
+ Money fs = this->feeder_share * new_size / static_cast(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);
+ this->feeder_share -= fs;
+ this->count -= new_size;
+ return cp_new;
+}
+
+void CargoPacket::Merge(CargoPacket *cp)
+{
+ this->count += cp->count;
+ this->feeder_share += cp->feeder_share;
+ delete cp;
+}
+
/**
* Invalidates (sets source to INVALID_STATION) all cargo packets from given station
* @param sid the station that gets removed
@@ -90,37 +107,32 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
*
*/
-template
-CargoList::~CargoList()
+template
+CargoList::~CargoList()
{
for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
delete *it;
}
}
-template
-void CargoList::RemoveFromCache(const CargoPacket *cp)
+template
+void CargoList::RemoveFromCache(const CargoPacket *cp)
{
this->count -= cp->count;
this->cargo_days_in_transit -= cp->days_in_transit * cp->count;
}
-template
-void CargoList::AddToCache(const CargoPacket *cp)
+template
+void CargoList::AddToCache(const CargoPacket *cp)
{
this->count += cp->count;
this->cargo_days_in_transit += cp->days_in_transit * cp->count;
}
-
-template
-void CargoList::Append(CargoPacket *cp)
+void VehicleCargoList::MergeOrPush(CargoPacket *cp)
{
- assert(cp != NULL);
- static_cast(this)->AddToCache(cp);
-
- for (List::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
+ for (CargoPacketList::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) {
+ if (VehicleCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
icp->count += cp->count;
icp->feeder_share += cp->feeder_share;
@@ -134,8 +146,16 @@ void CargoList::Append(CargoPacket *cp)
}
-template
-void CargoList::Truncate(uint max_remaining)
+void VehicleCargoList::Append(CargoPacket *cp)
+{
+ assert(cp != NULL);
+ this->AddToCache(cp);
+ this->MergeOrPush(cp);
+}
+
+
+template
+void CargoList::Truncate(uint max_remaining)
{
for (Iterator it(packets.begin()); it != packets.end(); /* done during loop*/) {
CargoPacket *cp = *it;
@@ -161,87 +181,94 @@ void CargoList::Truncate(uint max_remaining)
}
}
-template
-template
-bool CargoList::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta, CargoPayment *payment, uint data)
+void VehicleCargoList::Reserve(CargoPacket *cp)
{
- assert(mta == MTA_FINAL_DELIVERY || dest != NULL);
- assert(mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL);
+ assert(cp != NULL);
+ this->AddToCache(cp);
+ this->reserved_count += cp->count;
+ this->reserved.push_back(cp);
+}
- Iterator it(this->packets.begin());
- while (it != this->packets.end() && max_move > 0) {
+
+void VehicleCargoList::Unreserve(StationID next, StationCargoList *dest)
+{
+ Iterator it(this->reserved.begin());
+ while (it != this->reserved.end()) {
CargoPacket *cp = *it;
- if (cp->source == data && mta == MTA_FINAL_DELIVERY) {
- /* Skip cargo that originated from this station. */
- ++it;
- continue;
- }
+ this->RemoveFromCache(cp);
+ this->reserved_count -= cp->count;
+ dest->Append(next, cp);
+ this->reserved.erase(it++);
+ }
+}
+uint VehicleCargoList::LoadReserved(uint max_move)
+{
+ uint orig_max = max_move;
+ Iterator it(this->reserved.begin());
+ while (it != this->reserved.end() && max_move > 0) {
+ CargoPacket *cp = *it;
if (cp->count <= max_move) {
/* Can move the complete packet */
max_move -= cp->count;
- this->packets.erase(it++);
- static_cast(this)->RemoveFromCache(cp);
- 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 */
- uint left = cp->count - max_move;
- cp->count = max_move;
- static_cast(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;
+ this->reserved.erase(it++);
+ this->reserved_count -= cp->count;
+ this->MergeOrPush(cp);
} else {
- /* But... the rest needs package splitting. */
- Money fs = cp->feeder_share * max_move / static_cast(cp->count);
- cp->feeder_share -= fs;
cp->count -= max_move;
-
- CargoPacket *cp_new = new CargoPacket(max_move, cp->days_in_transit, cp->source, cp->source_xy, (mta == MTA_CARGO_LOAD) ? data : cp->loaded_at_xy, fs, cp->source_type, cp->source_id);
- static_cast(this)->RemoveFromCache(cp_new); // 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);
- }
-
- dest->Append(cp_new);
+ CargoPacket *cp_new = new CargoPacket(max_move, cp->days_in_transit, cp->source, cp->source_xy, cp->loaded_at_xy, 0, cp->source_type, cp->source_id);
+ this->MergeOrPush(cp_new);
+ this->reserved_count -= max_move;
+ max_move = 0;
}
+ }
+ return orig_max - max_move;
+}
- max_move = 0;
+template
+uint CargoList::MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place, bool reserve)
+{
+ CargoPacket *packet = MovePacket(it, cap, load_place);
+ uint ret = packet->count;
+ if (reserve) {
+ dest->Reserve(packet);
+ } else {
+ dest->Append(packet);
}
+ return ret;
+}
+
+template
+uint CargoList::MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap)
+{
+ CargoPacket *packet = MovePacket(it, cap);
+ uint ret = packet->count;
+ dest->Append(next, packet);
+ return ret;
+}
- return it != packets.end();
+template
+CargoPacket *CargoList::MovePacket(Iterator &it, uint cap, TileIndex load_place)
+{
+ CargoPacket *packet = *it;
+ /* load the packet if possible */
+ if (packet->count > cap) {
+ /* packet needs to be split */
+ packet = packet->Split(cap);
+ assert(packet->count == cap);
+ ++it;
+ } else {
+ this->packets.erase(it++);
+ }
+ static_cast(this)->RemoveFromCache(packet);
+ if (load_place != INVALID_TILE) {
+ packet->loaded_at_xy = load_place;
+ }
+ return packet;
}
-template
-void CargoList::InvalidateCache()
+template
+void CargoList::InvalidateCache()
{
this->count = 0;
this->cargo_days_in_transit = 0;
@@ -251,6 +278,186 @@ void CargoList::InvalidateCache()
}
}
+VehicleCargoList::~VehicleCargoList()
+{
+ for (Iterator it(this->reserved.begin()); it != this->reserved.end(); ++it) {
+ delete *it;
+ }
+}
+
+uint VehicleCargoList::DeliverPacket(Iterator &c, uint remaining_unload, CargoPayment *payment) {
+ CargoPacket * p = *c;
+ uint loaded = 0;
+ if (p->count <= remaining_unload) {
+ payment->PayFinalDelivery(p, p->count);
+ packets.erase(c++);
+ this->RemoveFromCache(p);
+ loaded = p->count;
+ delete p;
+ } else {
+ payment->PayFinalDelivery(p, remaining_unload);
+ this->count -= remaining_unload;
+ this->cargo_days_in_transit -= remaining_unload * p->days_in_transit;
+ this->feeder_share -= p->feeder_share;
+ p->feeder_share = 0;
+ p->count -= remaining_unload;
+ loaded = remaining_unload;
+ ++c;
+ }
+ return loaded;
+}
+
+uint VehicleCargoList::KeepPacket(Iterator &c)
+{
+ CargoPacket *cp = *c;
+ this->reserved.push_back(cp);
+ this->reserved_count += cp->count;
+ this->packets.erase(c++);
+ return cp->count;
+}
+
+
+uint VehicleCargoList::TransferPacket(Iterator &c, uint remaining_unload, GoodsEntry *dest, CargoPayment *payment, StationID next)
+{
+ CargoPacket *p = this->MovePacket(c, remaining_unload);
+ p->feeder_share += payment->PayTransfer(p, p->count);
+ uint ret = p->count;
+ dest->cargo.Append(next, p);
+ SetBit(dest->acceptance_pickup, GoodsEntry::PICKUP);
+ return ret;
+}
+
+/* static */ UnloadType VehicleCargoList::WillUnloadOld(byte flags, StationID curr_station, StationID source)
+{
+ /* try to unload cargo */
+ bool move = (flags & (UL_DELIVER | UL_ACCEPTED | UL_TRANSFER)) != 0;
+ /* try to deliver cargo if unloading */
+ bool deliver = (flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && (source != curr_station);
+ /* transfer cargo if delivery was unsuccessful */
+ bool transfer = (flags & (UL_TRANSFER | UL_DELIVER)) != 0;
+ if (move) {
+ if(deliver) {
+ return UL_DELIVER;
+ } else if (transfer) {
+ return UL_TRANSFER;
+ } else {
+ /* this case is for (non-)delivery to the source station without special flags.
+ * like the code in MoveTo did, we keep the packet in this case
+ */
+ return UL_KEEP;
+ }
+ } else {
+ return UL_KEEP;
+ }
+}
+
+/* static */ UnloadType VehicleCargoList::WillUnloadCargoDist(byte flags, StationID curr_station, StationID next_station, StationID via, StationID source)
+{
+ if (via == curr_station) {
+ /* this is the final destination, deliver ... */
+ if (flags & UL_TRANSFER) {
+ /* .. except if explicitly told not to do so ... */
+ return UL_TRANSFER;
+ } else if (flags & UL_ACCEPTED) {
+ return UL_DELIVER;
+ } else if (flags & UL_DELIVER) {
+ /* .. or if the station suddenly doesn't accept our cargo, but we have an explicit deliver order... */
+ return UL_TRANSFER;
+ } else {
+ /* .. or else if it doesn't accept. */
+ return UL_KEEP;
+ }
+ } else {
+ /* packet has to travel on, find out if it can stay on board */
+ if (flags & UL_DELIVER) {
+ /* order overrides cargodist:
+ * play by the old loading rules here as player is interfering with cargodist
+ * try to deliver, as move has been forced upon us */
+ if ((flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && source != curr_station) {
+ return UL_DELIVER;
+ } else {
+ /* transfer cargo, as delivering didn't work */
+ /* plan might still be fulfilled as the packet can be picked up by another vehicle travelling to "via" */
+ return UL_TRANSFER;
+ }
+ } else if (flags & UL_TRANSFER) {
+ /* transfer forced, plan still fulfilled as above */
+ return UL_TRANSFER;
+ } else if (next_station == via) {
+ /* vehicle goes to the packet's next hop or has nondeterministic order: keep the packet*/
+ return UL_KEEP;
+ } else {
+ /* vehicle goes somewhere else, transfer the packet*/
+ return UL_TRANSFER;
+ }
+ }
+}
+
+void VehicleCargoList::SwapReserved()
+{
+ assert(this->packets.empty());
+ this->packets.swap(this->reserved);
+ this->reserved_count = 0;
+}
+
+uint VehicleCargoList::MoveToStation(GoodsEntry * dest, uint max_unload, OrderUnloadFlags order_flags, StationID curr_station, StationID next_station, CargoPayment *payment) {
+ uint remaining_unload = max_unload;
+ uint unloaded;
+ UnloadType action;
+ byte flags = GetUnloadFlags(dest, order_flags);
+
+ for(Iterator c = packets.begin(); c != packets.end() && remaining_unload > 0;) {
+ StationID source = (*c)->source;
+ FlowStatSet &flows = dest->flows[source];
+ FlowStatSet::iterator begin = flows.begin();
+ StationID via = (begin != flows.end() ? begin->Via() : INVALID_STATION);
+ if (via != INVALID_STATION && next_station != INVALID_STATION) {
+ /* use cargodist unloading*/
+ action = WillUnloadCargoDist(flags, curr_station, next_station, via, source);
+ } else {
+ /* there is no plan: use normal unloading */
+ action = WillUnloadOld(flags, curr_station, source);
+ }
+
+ switch(action) {
+ case UL_DELIVER:
+ unloaded = this->DeliverPacket(c, remaining_unload, payment);
+ if (via != INVALID_STATION) {
+ if (via == curr_station) {
+ dest->UpdateFlowStats(flows, begin, unloaded);
+ } else {
+ dest->UpdateFlowStats(flows, unloaded, curr_station);
+ }
+ }
+ remaining_unload -= unloaded;
+ break;
+ case UL_TRANSFER:
+ /* TransferPacket may split the packet and return the transferred part */
+ if (via == curr_station) {
+ via = (++begin != flows.end()) ? begin->Via() : INVALID_STATION;
+ }
+ unloaded = this->TransferPacket(c, remaining_unload, dest, payment, via);
+ if (via != INVALID_STATION) {
+ dest->UpdateFlowStats(flows, begin, unloaded);
+ }
+ remaining_unload -= unloaded;
+ break;
+ case UL_KEEP:
+ unloaded = this->KeepPacket(c);
+ if (via != INVALID_STATION && next_station != INVALID_STATION) {
+ if (via == next_station) {
+ dest->UpdateFlowStats(flows, begin, unloaded);
+ } else {
+ dest->UpdateFlowStats(flows, unloaded, next_station);
+ }
+ }
+ break;
+ default:
+ NOT_REACHED();
+ }
+ }
+ return max_unload - remaining_unload;
+}
void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
{
@@ -264,6 +471,17 @@ void VehicleCargoList::AddToCache(const CargoPacket *cp)
this->Parent::AddToCache(cp);
}
+uint VehicleCargoList::MoveTo(VehicleCargoList *dest, uint cap)
+{
+ uint orig_cap = cap;
+ Iterator it = packets.begin();
+ while(it != packets.end() && cap > 0) {
+ cap -= MovePacket(dest, it, cap);
+ }
+ return orig_cap - cap;
+}
+
+
void VehicleCargoList::AgeCargo()
{
for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
@@ -276,21 +494,97 @@ void VehicleCargoList::AgeCargo()
}
}
+/* static */ byte VehicleCargoList::GetUnloadFlags(GoodsEntry *dest, OrderUnloadFlags order_flags)
+{
+ byte flags = 0;
+ if (HasBit(dest->acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
+ flags |= UL_ACCEPTED;
+ }
+ if (order_flags & OUFB_UNLOAD) {
+ flags |= UL_DELIVER;
+ }
+ if (order_flags & OUFB_TRANSFER) {
+ flags |= UL_TRANSFER;
+ }
+ return flags;
+}
+
+/*
+ *
+ * Station cargo list implementation
+ *
+ */
+
+void StationCargoList::Append(StationID next, CargoPacket *cp)
+{
+ assert(cp != NULL);
+ this->AddToCache(cp);
+ StationCargoPacketMap::List &list = this->packets[next];
+
+ for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); it != list.rend(); it++) {
+ CargoPacket *icp = *it;
+ if (StationCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
+ icp->Merge(cp);
+ return;
+ }
+ }
+
+ /* The packet could not be merged with another one */
+ list.push_back(cp);
+}
+
+uint StationCargoList::MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, TileIndex load_place, bool reserve) {
+ uint orig_cap = cap;
+ while(begin != end && cap > 0) {
+ cap -= this->MovePacket(dest, begin, cap, load_place, reserve);
+ }
+ return orig_cap - cap;
+}
+
+uint StationCargoList::MoveTo(VehicleCargoList *dest, uint cap, StationID selected_station, TileIndex load_place, bool reserve) {
+ uint orig_cap = cap;
+ if (selected_station != INVALID_STATION) {
+ std::pair bounds(packets.equal_range(selected_station));
+ cap -= MovePackets(dest, cap, bounds.first, bounds.second, load_place, reserve);
+ if (cap > 0) {
+ bounds = packets.equal_range(INVALID_STATION);
+ cap -= MovePackets(dest, cap, bounds.first, bounds.second, load_place, reserve);
+ }
+ } else {
+ cap -= MovePackets(dest, cap, packets.begin(), packets.end(), load_place, reserve);
+ }
+ return orig_cap - cap;
+}
+
+void StationCargoList::RerouteStalePackets(StationID curr, StationID to, GoodsEntry * ge) {
+ std::pair range(packets.equal_range(to));
+ for(Iterator it(range.first); it != range.second && it.GetKey() == to;) {
+ CargoPacket * packet = *it;
+ packets.erase(it++);
+ StationID next = ge->UpdateFlowStatsTransfer(packet->source, packet->count, curr);
+ assert(next != to);
+
+ /* legal, as insert doesn't invalidate iterators in the MultiMap, however
+ * this might insert the packet between range.first and range.second (which might be end())
+ * This is why we check for GetKey above to avoid infinite loops
+ */
+ packets.Insert(next, packet);
+ }
+}
+
void VehicleCargoList::InvalidateCache()
{
this->feeder_share = 0;
+ this->reserved_count = 0;
this->Parent::InvalidateCache();
+ for (ConstIterator it(this->reserved.begin()); it != this->reserved.end(); it++) {
+ this->AddToCache(*it);
+ this->reserved_count += (*it)->count;
+ }
}
/*
* We have to instantiate everything we want to be usable.
*/
-template class CargoList;
-template class CargoList;
-
-/** Autoreplace Vehicle -> Vehicle 'transfer' */
-template bool CargoList::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
-/** Cargo unloading at a station */
-template bool CargoList::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
-/** Cargo loading at a station */
-template bool CargoList::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
+template class CargoList;
+template class CargoList;
diff --git src/cargopacket.h src/cargopacket.h
index dfd5e72..a6da5fe 100644
--- src/cargopacket.h
+++ src/cargopacket.h
@@ -15,20 +15,25 @@
#include "core/pool_type.hpp"
#include "economy_type.h"
#include "station_type.h"
+#include "order_type.h"
#include "cargo_type.h"
#include "vehicle_type.h"
+#include "core/multimap.hpp"
#include
/** Unique identifier for a single cargo packet. */
typedef uint32 CargoPacketID;
struct CargoPacket;
+struct GoodsEntry;
/** Type of the pool for cargo packets. */
typedef Pool CargoPacketPool;
/** The actual pool with cargo packets */
extern CargoPacketPool _cargopacket_pool;
-template class CargoList;
+template class CargoList;
+class StationCargoList;
+class VehicleCargoList;
extern const struct SaveLoad *GetCargoPacketDesc();
/**
@@ -46,8 +51,9 @@ private:
TileIndex loaded_at_xy; ///< Location where this cargo has been loaded into the vehicle
/** The CargoList caches, thus needs to know about it. */
- template friend class CargoList;
+ template friend class CargoList;
friend class VehicleCargoList;
+ friend class ReservationList;
friend class StationCargoList;
/** We want this to be saved, right? */
friend const struct SaveLoad *GetCargoPacketDesc();
@@ -164,6 +170,8 @@ public:
return this->loaded_at_xy;
}
+ CargoPacket *Split(uint new_size);
+ void Merge(CargoPacket *other);
static void InvalidateAllFrom(SourceType src_type, SourceID src);
static void InvalidateAllFrom(StationID sid);
@@ -183,33 +191,37 @@ public:
*/
#define FOR_ALL_CARGOPACKETS(var) FOR_ALL_CARGOPACKETS_FROM(var, 0)
+enum UnloadType {
+ UL_KEEP = 0, ///< keep cargo on vehicle
+ UL_DELIVER = 1 << 0, ///< deliver cargo
+ UL_TRANSFER = 1 << 1, ///< transfer cargo
+ UL_ACCEPTED = 1 << 2, ///< cargo is accepted
+};
+
+class StationCargoList;
+class VehicleCargoList;
+
/**
* Simple collection class for a list of cargo packets
* @tparam Tinst The actual instantation of this cargo list
*/
-template
+template
class CargoList {
public:
- /** Container with cargo packets */
- typedef std::list List;
/** The iterator for our container */
- typedef List::iterator Iterator;
+ typedef typename Tcont::iterator Iterator;
/** The const iterator for our container */
- typedef List::const_iterator ConstIterator;
-
- /** 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
- };
+ typedef typename Tcont::const_iterator ConstIterator;
+ /** The reverse iterator for our container */
+ typedef typename Tcont::reverse_iterator ReverseIterator;
+ /** The const reverse iterator for our container */
+ typedef typename Tcont::const_reverse_iterator ConstReverseIterator;
protected:
uint count; ///< Cache for the number of cargo entities
uint cargo_days_in_transit; ///< Cache for the sum of number of days in transit of each entity; comparable to man-hours
- List packets; ///< The cargo packets in this list
+ Tcont packets; ///< The cargo packets in this list
/**
* Update the cache to reflect adding of this packet.
@@ -225,6 +237,12 @@ protected:
*/
void RemoveFromCache(const CargoPacket *cp);
+ CargoPacket *MovePacket(Iterator &it, uint cap, TileIndex load_place = INVALID_TILE);
+
+ uint MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap);
+
+ uint MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place = INVALID_TILE, bool reserved = false);
+
public:
/** Create the cargo list */
CargoList() {}
@@ -235,7 +253,7 @@ public:
* Returns a pointer to the cargo packet list (so you can iterate over it etc).
* @return pointer to the packet list
*/
- FORCEINLINE const List *Packets() const
+ FORCEINLINE const Tcont *Packets() const
{
return &this->packets;
}
@@ -259,15 +277,6 @@ public:
}
/**
- * Returns source of the first cargo packet in this list
- * @return the before mentioned source
- */
- FORCEINLINE StationID Source() const
- {
- return this->Empty() ? INVALID_STATION : this->packets.front()->source;
- }
-
- /**
* Returns average number of days in transit for a cargo entity
* @return the before mentioned number
*/
@@ -276,16 +285,6 @@ public:
return this->count == 0 ? 0 : this->cargo_days_in_transit / this->count;
}
-
- /**
- * Appends the given cargo packet
- * @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 the cargo packet to add
- * @pre cp != NULL
- */
- void Append(CargoPacket *cp);
-
/**
* Truncates the cargo in this list to the given amount. It leaves the
* first count cargo entities and removes the rest.
@@ -293,43 +292,30 @@ public:
*/
void Truncate(uint max_remaining);
- /**
- * 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 the destination to move the cargo to
- * @param count the 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
- bool MoveTo(Tother_inst *dest, uint count, MoveToAction mta, CargoPayment *payment, uint data = 0);
-
/** Invalidates the cached data and rebuild it */
void InvalidateCache();
};
+typedef std::list CargoPacketList;
+
/**
* CargoList that is used for vehicles.
*/
-class VehicleCargoList : public CargoList {
+class VehicleCargoList : public CargoList {
protected:
+ static UnloadType WillUnloadOld(byte flags, StationID curr_station, StationID source);
+ static UnloadType WillUnloadCargoDist(byte flags, StationID curr_station, StationID next_station, StationID via, StationID source);
+
+ uint TransferPacket(Iterator &c, uint remaining_unload, GoodsEntry *dest, CargoPayment *payment, StationID next);
+ uint DeliverPacket(Iterator &c, uint remaining_unload, CargoPayment *payment);
+ uint KeepPacket(Iterator &c);
+
/** The (direct) parent of this class */
- typedef CargoList Parent;
+ typedef CargoList Parent;
- Money feeder_share; ///< Cache for the feeder share
+ CargoPacketList reserved; ///< The packets reserved for unloading in this list
+ Money feeder_share; ///< Cache for the feeder share
+ uint reserved_count; ///< count(reserved)
/**
* Update the cache to reflect adding of this packet.
@@ -345,11 +331,37 @@ protected:
*/
void RemoveFromCache(const CargoPacket *cp);
+ static byte GetUnloadFlags(GoodsEntry *dest, OrderUnloadFlags order_flags);
+
public:
/** The super class ought to know what it's doing */
- friend class CargoList;
+ friend class CargoList;
/** The vehicles have a cargo list (and we want that saved). */
friend const struct SaveLoad *GetVehicleDescription(VehicleType vt);
+ /**
+ * Moves the given amount of cargo from a vehicle to a station.
+ * Depending on the value of flags and dest the side effects of this function differ:
+ * - dest->acceptance_pickup & GoodsEntry::ACCEPTANCE:
+ * => MoveToStation sets OUF_UNLOAD_IF_POSSIBLE in the flags
+ * packets are accepted here and may be unloaded and/or delivered (=destroyed);
+ * if not using cargodist: all packets are unloaded and delivered
+ * if using cargodist: only packets which have this station as final destination are unloaded and delivered
+ * if using cargodist: other packets may or may not be unloaded, depending on next_station
+ * if not set and using cargodist: packets may still be unloaded, but not delivered.
+ * - OUFB_UNLOAD: unload all packets unconditionally;
+ * if OUF_UNLOAD_IF_POSSIBLE set and OUFB_TRANSFER not set: also deliver packets (no matter if using cargodist)
+ * - OUFB_TRANSFER: don't deliver any packets;
+ * overrides delivering aspect of OUF_UNLOAD_IF_POSSIBLE
+ * @param dest the destination to move the cargo to
+ * @param max_unload the maximum amount of cargo entities to move
+ * @param flags how to handle the moving (side effects)
+ * @param curr_station the station where the cargo currently resides
+ * @param next_station the next unloading station in the vehicle's order list
+ * @return the number of cargo entities actually moved
+ */
+ uint MoveToStation(GoodsEntry * dest, uint max_unload, OrderUnloadFlags flags, StationID curr_station, StationID next_station, CargoPayment *payment);
+
+ ~VehicleCargoList();
/**
* Returns total sum of the feeder share for all packets
@@ -361,6 +373,92 @@ public:
}
/**
+ * tries to merge the packet with another one in the packets list.
+ * if no fitting packet is found, appends it.
+ * @param cp the packet to be inserted
+ */
+ void MergeOrPush(CargoPacket *cp);
+
+ /**
+ * Appends the given cargo packet
+ * @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 the cargo packet to add
+ * @param check_merge if true, check existing packets in the list for mergability
+ * @pre cp != NULL
+ */
+ void Append(CargoPacket *cp);
+
+ /**
+ * Returns sum of cargo on board the vehicle (ie not only
+ * reserved)
+ * @return cargo on board the vehicle
+ */
+ FORCEINLINE uint OnboardCount() const
+ {
+ return this->count - this->reserved_count;
+ }
+
+ /**
+ * Returns sum of cargo reserved for the vehicle
+ * @return cargo reserved for the vehicle
+ */
+ FORCEINLINE uint ReservedCount() const
+ {
+ return this->reserved_count;
+ }
+
+ /**
+ * Returns a pointer to the reserved cargo list (so you can iterate over it etc).
+ * @return pointer to the reserved list
+ */
+ FORCEINLINE const CargoPacketList *Reserved() const
+ {
+ return &this->reserved;
+ }
+
+ /**
+ * Returns source of the first cargo packet in this list
+ * If the regular packets list is empty but there are packets
+ * in the reservation list it returns the source of the first
+ * reserved packet.
+ * @return the before mentioned source
+ */
+ FORCEINLINE StationID Source() const
+ {
+ if (this->Empty()) {
+ return INVALID_STATION;
+ } else if (this->packets.empty()) {
+ return this->reserved.front()->source;
+ } else {
+ return this->packets.front()->source;
+ }
+ }
+
+ /**
+ * Reserves a packet for later loading
+ */
+ void Reserve(CargoPacket *cp);
+
+ /**
+ * Returns all reserved cargo to the station
+ */
+ void Unreserve(StationID next, StationCargoList *dest);
+
+ /**
+ * load packets from the reserved list
+ * @params count the number of cargo to load
+ * @return true if there are still packets that might be loaded from the reservation list
+ */
+ uint LoadReserved(uint count);
+
+ /**
+ * swap the reserved and packets lists when starting to load cargo.
+ * @pre this->packets.empty()
+ */
+ void SwapReserved();
+
+ /**
* Ages the all cargo in this list
*/
void AgeCargo();
@@ -369,6 +467,13 @@ public:
void InvalidateCache();
/**
+ * Moves the given amount of cargo to another vehicle (during autoreplace).
+ * @param dest the destination to move the cargo to
+ * @param max_load the maximum amount of cargo entities to move
+ */
+ uint MoveTo(VehicleCargoList *dest, uint cap);
+
+ /**
* Are two the two CargoPackets mergeable in the context of
* a list of CargoPackets for a Vehicle?
* @param cp1 the first CargoPacket
@@ -385,17 +490,28 @@ public:
}
};
+typedef MultiMap StationCargoPacketMap;
+
/**
* CargoList that is used for stations.
*/
-class StationCargoList : public CargoList {
+class StationCargoList : public CargoList {
public:
/** The super class ought to know what it's doing */
- friend class CargoList;
+ friend class CargoList;
/** The stations, via GoodsEntry, have a CargoList. */
friend const struct SaveLoad *GetGoodsDesc();
/**
+ * Returns source of the first cargo packet in this list
+ * @return the before mentioned source
+ */
+ FORCEINLINE StationID Source() const
+ {
+ return this->Empty() ? INVALID_STATION : this->packets.begin()->second.front()->source;
+ }
+
+ /**
* Are two the two CargoPackets mergeable in the context of
* a list of CargoPackets for a Vehicle?
* @param cp1 the first CargoPacket
@@ -409,6 +525,28 @@ public:
cp1->source_type == cp2->source_type &&
cp1->source_id == cp2->source_id;
}
+
+ uint MoveTo(VehicleCargoList *dest, uint cap, StationID next_station, TileIndex load_place = INVALID_TILE, bool reserve = false);
+
+ /**
+ * Appends the given cargo packet to the range of packets with the same next station
+ * @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 next the next hop
+ * @param cp the cargo packet to add
+ * @pre cp != NULL
+ */
+ void Append(StationID next, CargoPacket *cp);
+
+ /**
+ * route all packets with station "to" as next hop to a different place, except "curr"
+ */
+ void RerouteStalePackets(StationID curr, StationID to, GoodsEntry * ge);
+
+ static void InvalidateAllFrom(SourceType src_type, SourceID src);
+
+protected:
+ uint MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, TileIndex load_place, bool reserve);
};
#endif /* CARGOPACKET_H */
diff --git src/cargotype.cpp src/cargotype.cpp
index 3e2e742..c3745e8 100644
--- src/cargotype.cpp
+++ src/cargotype.cpp
@@ -19,6 +19,7 @@
#include "table/cargo_const.h"
CargoSpec CargoSpec::array[NUM_CARGO];
+GlobalCargoAcceptance GlobalCargoAcceptance::inst;
/** Bitmask of cargo types available.
* Initialized during a call to #SetupCargoForClimate.
diff --git src/cargotype.h src/cargotype.h
index 29c7b54..a1a531d 100644
--- src/cargotype.h
+++ src/cargotype.h
@@ -131,6 +131,9 @@ void SetupCargoForClimate(LandscapeID l);
CargoID GetCargoIDByLabel(CargoLabel cl);
CargoID GetCargoIDByBitnum(uint8 bitnum);
+/* set up the cargos to be displayed in the smallmap's route legend */
+void BuildLinkStatsLegend();
+
/** Does cargo \a c have cargo class \a cc?
* @param c Cargo type.
* @param cc Cargo class.
diff --git src/command.cpp src/command.cpp
index 430f9d2..7e21ed0 100644
--- src/command.cpp
+++ src/command.cpp
@@ -186,6 +186,7 @@ CommandProc CmdMoveOrder;
CommandProc CmdChangeTimetable;
CommandProc CmdSetVehicleOnTime;
CommandProc CmdAutofillTimetable;
+CommandProc CmdAutomateTimetable;
CommandProc CmdSetTimetableStart;
/**
@@ -330,6 +331,7 @@ static const Command _command_proc_table[] = {
{CmdChangeTimetable, 0}, // CMD_CHANGE_TIMETABLE
{CmdSetVehicleOnTime, 0}, // CMD_SET_VEHICLE_ON_TIME
{CmdAutofillTimetable, 0}, // CMD_AUTOFILL_TIMETABLE
+ {CmdAutomateTimetable, 0}, // CMD_AUTOMATE_TIMETABLE
{CmdSetTimetableStart, 0}, // CMD_SET_TIMETABLE_START
};
diff --git src/command_type.h src/command_type.h
index fb71eac..2234ec0 100644
--- src/command_type.h
+++ src/command_type.h
@@ -294,6 +294,7 @@ enum {
CMD_CHANGE_TIMETABLE, ///< change the timetable for a vehicle
CMD_SET_VEHICLE_ON_TIME, ///< set the vehicle on time feature (timetable)
CMD_AUTOFILL_TIMETABLE, ///< autofill the timetable
+ CMD_AUTOMATE_TIMETABLE, ///< automate the timetable
CMD_SET_TIMETABLE_START, ///< set the date that a timetable should start
};
diff --git src/core/math_func.cpp src/core/math_func.cpp
index 7f0630a..1a9f398 100644
--- src/core/math_func.cpp
+++ src/core/math_func.cpp
@@ -46,3 +46,21 @@ int GreatestCommonDivisor(int a, int b)
return a;
}
+
+
+/**
+ * Deterministic approximate division.
+ * Cancels out division errors stemming from the integer nature of the division over multiple runs.
+ */
+int DivideApprox(int a, int b) {
+ int random_like = ((a + b) * (a - b)) % b;
+
+ int remainder = a % b;
+
+ int ret = a / b;
+ if (abs(random_like) < abs(remainder)) {
+ ret += ((a < 0) ^ (b < 0)) ? -1 : 1;
+ }
+
+ return ret;
+}
diff --git src/core/math_func.hpp src/core/math_func.hpp
index e0b9dbc..9d7319c 100644
--- src/core/math_func.hpp
+++ src/core/math_func.hpp
@@ -317,5 +317,6 @@ static FORCEINLINE uint ToPercent16(uint i)
int LeastCommonMultiple(int a, int b);
int GreatestCommonDivisor(int a, int b);
+int DivideApprox(int a, int b);
#endif /* MATH_FUNC_HPP */
diff --git src/core/multimap.hpp src/core/multimap.hpp
new file mode 100644
index 0000000..58e8e97
--- /dev/null
+++ src/core/multimap.hpp
@@ -0,0 +1,245 @@
+/*
+ * multimap.hpp
+ *
+ * Created on: Sep 15, 2009
+ * Author: alve
+ */
+
+#ifndef MULTIMAP_HPP_
+#define MULTIMAP_HPP_
+
+#include