@@ -207,13 +207,14 @@ void DemandCalculator::CalcDemand(LinkGr
int32 supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
assert(supply > 0);
/* Scale the distance by mod_dist around max_distance */
int32 distance = this->max_distance - (this->max_distance -
(int32)job[from_id][to_id].Distance()) * this->mod_dist / 100;
(int32)DistanceMaxPlusManhattan(job[from_id].XY(), job[to_id].XY())) *
this->mod_dist / 100;
/* Scale the accuracy by distance around accuracy / 2 */
int32 divisor = this->accuracy * (this->mod_dist - 50) / 100 +
this->accuracy * distance / this->max_distance + 1;
assert(divisor > 0);
@@ -18,30 +18,30 @@
/* Initialize the link-graph-pool */
LinkGraphPool _link_graph_pool("LinkGraph");
INSTANTIATE_POOL_METHODS(LinkGraph)
/**
* Create a node or clear it.
* @param xy Location of the associated station.
* @param st ID of the associated station.
* @param demand Demand for cargo at the station.
*/
inline void LinkGraph::BaseNode::Init(StationID st, uint demand)
inline void LinkGraph::BaseNode::Init(TileIndex xy, StationID st, uint demand)
{
this->xy = xy;
this->supply = 0;
this->demand = demand;
this->station = st;
this->last_update = INVALID_DATE;
}
* Create an edge.
* @param distance Length of the link as manhattan distance.
inline void LinkGraph::BaseEdge::Init(uint distance)
inline void LinkGraph::BaseEdge::Init()
this->distance = distance;
this->capacity = 0;
this->usage = 0;
this->last_unrestricted_update = INVALID_DATE;
this->last_restricted_update = INVALID_DATE;
this->next_edge = INVALID_NODE;
@@ -144,27 +144,12 @@ void LinkGraph::RemoveNode(NodeID id)
/* Not doing EraseRow here, as having the extra invalid row doesn't hurt
* and removing it would trigger a lot of memmove. The data has already
* been copied around in the loop above. */
* Update distances between the given node and all others.
* @param id Node that changed position.
* @param xy New position of the node.
void LinkGraph::UpdateDistances(NodeID id, TileIndex xy)
assert(id < this->Size());
for (NodeID other = 0; other < this->Size(); ++other) {
if (other == id) continue;
this->edges[id][other].distance = this->edges[other][id].distance =
DistanceMaxPlusManhattan(xy, Station::Get(this->nodes[other].station)->xy);
* Add a node to the component and create empty edges associated with it. Set
* the station's last_component to this component. Calculate the distances to all
* other nodes. The distances to _all_ nodes are important as the demand
* calculator relies on their availability.
* @param st New node's station.
* @return New node's ID.
@@ -177,24 +162,23 @@ NodeID LinkGraph::AddNode(const Station
this->nodes.Append();
/* Avoid reducing the height of the matrix as that is expensive and we
* most likely will increase it again later which is again expensive. */
this->edges.Resize(new_node + 1U,
max(new_node + 1U, this->edges.Height()));
this->nodes[new_node].Init(st->index,
this->nodes[new_node].Init(st->xy, st->index,
HasBit(good.status, GoodsEntry::GES_ACCEPTANCE));
BaseEdge *new_edges = this->edges[new_node];
/* Reset the first edge starting at the new node */
new_edges[new_node].next_edge = INVALID_NODE;
for (NodeID i = 0; i <= new_node; ++i) {
uint distance = DistanceMaxPlusManhattan(st->xy, Station::Get(this->nodes[i].station)->xy);
new_edges[i].Init(distance);
this->edges[i][new_node].Init(distance);
new_edges[i].Init();
this->edges[i][new_node].Init();
return new_node;
* Fill an edge with values from a link. Set the restricted or unrestricted
@@ -46,30 +46,30 @@ public:
* in a separate thread.
struct BaseNode {
uint supply; ///< Supply at the station.
uint demand; ///< Acceptance at the station.
StationID station; ///< Station ID.
TileIndex xy; ///< Location of the station referred to by the node.
Date last_update; ///< When the supply was last updated.
void Init(StationID st = INVALID_STATION, uint demand = 0);
void Init(TileIndex xy = INVALID_TILE, StationID st = INVALID_STATION, uint demand = 0);
};
* An edge in the link graph. Corresponds to a link between two stations or at
* least the distance between them. Edges from one node to itself contain the
* ID of the opposite Node of the first active edge (i.e. not just distance) in
* the column as next_edge.
struct BaseEdge {
uint distance; ///< Length of the link.
uint capacity; ///< Capacity of the link.
uint usage; ///< Usage of the link.
Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated.
Date last_restricted_update; ///< When the restricted part of the link was last updated.
NodeID next_edge; ///< Destination of next valid edge starting at the same source node.
void Init(uint distance = 0);
void Init();
* Wrapper for an edge (const or not) allowing retrieval, but no modification.
* @tparam Tedge Actual edge class, may be "const BaseEdge" or just "BaseEdge".
@@ -96,18 +96,12 @@ public:
* Get edge's usage.
* @return Usage.
uint Usage() const { return this->edge.usage; }
* Get edge's distance.
* @return Distance.
uint Distance() const { return this->edge.distance; }
* Get the date of the last update to the edge's unrestricted capacity.
* @return Last update.
Date LastUnrestrictedUpdate() const { return this->edge.last_unrestricted_update; }
@@ -166,12 +160,18 @@ public:
* Get node's last update.
Date LastUpdate() const { return this->node.last_update; }
* Get the location of the station associated with the node.
* @return Location of the station.
TileIndex XY() const { return this->node.xy; }
* Base class for iterating across outgoing edges of a node. Only the real
* edges (those with capacity) are iterated. The ones with only distance
* information are skipped.
@@ -410,12 +410,21 @@ public:
this->node.supply += supply;
this->node.last_update = _date;
* Update the node's location on the map.
* @param xy New location.
void UpdateLocation(TileIndex xy)
this->node.xy = xy;
* Set the node's demand.
* @param demand New demand for the node.
void SetDemand(uint demand)
this->node.demand = demand;
@@ -510,13 +519,12 @@ public:
return base * 30 / (_date - this->last_compression + 1);
NodeID AddNode(const Station *st);
void RemoveNode(NodeID id);
void UpdateDistances(NodeID id, TileIndex xy);
protected:
friend class LinkGraph::ConstNode;
friend class LinkGraph::Node;
friend const SaveLoad *GetLinkGraphDesc();
friend const SaveLoad *GetLinkGraphJobDesc();
@@ -256,21 +256,20 @@ void MultiCommodityFlow::Dijkstra(NodeID
annos.erase(i);
NodeID from = source->GetNode();
iter.SetNode(source_node, from);
for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
if (to == from) continue; // Not a real edge but a consumption sign.
Edge edge = this->job[from][to];
assert(edge.Distance() < UINT_MAX);
uint capacity = edge.Capacity();
if (this->max_saturation != UINT_MAX) {
capacity *= this->max_saturation;
capacity /= 100;
if (capacity == 0) capacity = 1;
/* punish in-between stops a little */
uint distance = edge.Distance() + 1;
uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1;
Tannotation *dest = static_cast<Tannotation *>(paths[to]);
if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) {
annos.erase(dest);
dest->Fork(source, capacity, capacity - edge.Flow(), distance);
annos.insert(dest);
@@ -106,44 +106,53 @@ const SaveLoad *GetLinkGraphScheduleDesc
/* Edges and nodes are saved in the correct order, so we don't need to save their IDs. */
* SaveLoad desc for a link graph node.
static const SaveLoad _node_desc[] = {
SLE_VAR(Node, supply, SLE_UINT32),
SLE_VAR(Node, demand, SLE_UINT32),
SLE_VAR(Node, station, SLE_UINT16),
SLE_VAR(Node, last_update, SLE_INT32),
SLE_END()
SLE_CONDVAR(Node, xy, SLE_UINT32, 191, SL_MAX_VERSION),
* SaveLoad desc for a link graph edge.
static const SaveLoad _edge_desc[] = {
SLE_VAR(Edge, distance, SLE_UINT32),
SLE_VAR(Edge, capacity, SLE_UINT32),
SLE_VAR(Edge, usage, SLE_UINT32),
SLE_VAR(Edge, last_unrestricted_update, SLE_INT32),
SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, 187, SL_MAX_VERSION),
SLE_VAR(Edge, next_edge, SLE_UINT16),
SLE_CONDNULL(4, 0, 190), // distance
* Save/load a link graph.
* @param comp Link graph to be saved or loaded.
void SaveLoad_LinkGraph(LinkGraph &lg)
uint size = lg.Size();
for (NodeID from = 0; from < size; ++from) {
Node *node = &lg.nodes[from];
SlObject(node, _node_desc);
for (NodeID to = 0; to < size; ++to) {
SlObject(&lg.edges[from][to], _edge_desc);
if (IsSavegameVersionBefore(191)) {
/* We used to save the full matrix ... */
} else {
/* ... but as that wasted a lot of space we save a sparse matrix now. */
for (NodeID to = from; to != INVALID_NODE; to = lg.edges[from][to].next_edge) {
* Save a link graph job.
@@ -217,12 +226,29 @@ static void Load_LGRS()
* Spawn the threads for running link graph calculations.
* Has to be done after loading as the cargo classes might have changed.
void AfterLoadLinkGraphs()
LinkGraph *lg;
FOR_ALL_LINK_GRAPHS(lg) {
for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) {
(*lg)[node_id].UpdateLocation(Station::Get((*lg)[node_id].Station())->xy);
LinkGraphJob *lgj;
FOR_ALL_LINK_GRAPH_JOBS(lgj) {
lg = &(const_cast<LinkGraph &>(lgj->Graph()));
LinkGraphSchedule::Instance()->SpawnAll();
* Save all link graphs.
@@ -255,14 +255,15 @@
* 185 25620
* 186 25833
* 187 25899
* 188 26169 1.4.x
* 189 26450
* 190 26547
* 191 26646
extern const uint16 SAVEGAME_VERSION = 190; ///< Current savegame version of OpenTTD.
extern const uint16 SAVEGAME_VERSION = 191; ///< Current savegame version of OpenTTD.
SavegameType _savegame_type; ///< type of savegame we are loading
uint32 _ttdp_version; ///< version of TTDP savegame (if applicable)
uint16 _sl_version; ///< the major savegame version identifier
byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
@@ -646,13 +646,13 @@ static void UpdateStationSignCoord(BaseS
if (!Station::IsExpected(st)) return;
Station *full_station = Station::From(st);
for (CargoID c = 0; c < NUM_CARGO; ++c) {
LinkGraphID lg = full_station->goods[c].link_graph;
if (!LinkGraph::IsValidID(lg)) continue;
LinkGraph::Get(lg)->UpdateDistances(full_station->goods[c].node, st->xy);
(*LinkGraph::Get(lg))[full_station->goods[c].node].UpdateLocation(st->xy);
* Common part of building various station parts and possibly attaching them to an existing one.
* @param [in,out] st Station to attach to
Status change: