Fix: only count distance traveled in vehicles for cargo payment (#11283)

No longer you can utilize the free (and instant) labour of station
workers, transporting your cargo from one part of the station to
the other. No more!

Based on patch by dP.
This commit is contained in:
Patric Stout
2023-09-19 22:16:31 +02:00
committed by GitHub
parent 710722e3c1
commit df400ef84a
8 changed files with 141 additions and 21 deletions

View File

@@ -39,15 +39,27 @@ extern SaveLoadTable GetCargoPacketDesc();
*/
struct CargoPacket : CargoPacketPool::PoolItem<&_cargopacket_pool> {
private:
/* A mathematical vector from (0,0). */
struct Vector {
int16_t x;
int16_t y;
};
uint16_t count{0}; ///< The amount of cargo in this packet.
uint16_t periods_in_transit{0}; ///< Amount of cargo aging periods this packet has been in transit.
Money feeder_share{0}; ///< Value of feeder pickup to be paid for on delivery of cargo.
TileIndex source_xy{INVALID_TILE}; ///< The origin of the cargo.
Vector travelled{0, 0}; ///< If cargo is in station: the vector from the unload tile to the source tile. If in vehicle: an intermediate value.
SourceID source_id{INVALID_SOURCE}; ///< Index of industry/town/HQ, INVALID_SOURCE if unknown/invalid.
SourceType source_type{SourceType::Industry}; ///< Type of \c source_id.
#ifdef WITH_ASSERT
bool in_vehicle{false}; ///< NOSAVE: Whether this cargo is in a vehicle or not.
#endif /* WITH_ASSERT */
StationID first_station{INVALID_STATION}; ///< The station where the cargo came from first.
StationID next_hop{INVALID_STATION}; ///< Station where the cargo wants to go next.
@@ -83,22 +95,51 @@ public:
}
/**
* Set the origin of the packet.
* Update for the cargo being loaded on this tile.
*
* Can only be set once.
* When a CargoPacket is created, it is moved to a station. But at that
* moment in time it is not known yet at which tile the cargo will be
* picked up. As this tile is used for payment information, we delay
* setting the source_xy till first pickup, getting a better idea where
* a cargo started from.
*
* When a packet is created, it is moved to a station. But at that moment
* in time it is not known yet at which tile the cargo will be picked up.
* As this tile is used for payment information, we delay setting the
* source_xy till first pickup.
* Further more, we keep track of the amount of tiles the cargo moved
* inside a vehicle. This is used in GetDistance() below.
*
* @param tile Tile the cargo is being picked up from.
*/
void SetSourceXY(TileIndex tile)
void UpdateLoadingTile(TileIndex tile)
{
if (this->source_xy == INVALID_TILE) {
this->source_xy = tile;
}
#ifdef WITH_ASSERT
assert(!this->in_vehicle);
this->in_vehicle = true;
#endif /* WITH_ASSERT */
/* We want to calculate the vector from tile-unload to tile-load. As
* we currently only know the latter, add it. When we know where we unload,
* we subtract is, giving us our vector (unload - load). */
this->travelled.x += TileX(tile);
this->travelled.y += TileY(tile);
}
/**
* Update for the cargo being unloaded on this tile.
*
* @param tile Tile the cargo is being dropped off at.
*/
void UpdateUnloadingTile(TileIndex tile)
{
#ifdef WITH_ASSERT
assert(this->in_vehicle);
this->in_vehicle = false;
#endif /* WITH_ASSERT */
this->travelled.x -= TileX(tile);
this->travelled.y -= TileY(tile);
}
/**
@@ -188,7 +229,36 @@ public:
inline uint GetDistance(TileIndex current_tile) const
{
assert(this->source_xy != INVALID_TILE);
return DistanceManhattan(this->source_xy, current_tile);
#ifdef WITH_ASSERT
assert(this->in_vehicle);
#endif /* WITH_ASSERT */
/* Distance is always requested when the cargo is still inside the
* vehicle. So first finish the calculation for travelled to
* become a vector. */
auto local_travelled = travelled;
local_travelled.x -= TileX(current_tile);
local_travelled.y -= TileY(current_tile);
/* Cargo-movement is a vector that indicates how much the cargo has
* actually traveled in a vehicle. This is the distance you get paid
* for. However, one could construct a route where this vector would
* be really long. To not overpay the player, cap out at the distance
* between source and destination.
*
* This way of calculating is to counter people moving cargo for free
* and instantly in stations, where you deliver it in one part of the
* station and pick it up in another. By using the actual distance
* traveled in a vehicle, using this trick doesn't give you more money.
*
* However, especially in large networks with large transfer station,
* etc, one could actually make the route a lot longer. In that case,
* use the actual distance between source and destination.
*/
uint distance_travelled = abs(local_travelled.x) + abs(local_travelled.y);
uint distance_source_dest = DistanceManhattan(this->source_xy, current_tile);
return std::min(distance_travelled, distance_source_dest);
}
/**
@@ -427,7 +497,7 @@ public:
template<MoveToAction Tfrom, MoveToAction Tto>
uint Reassign(uint max_move);
uint Return(uint max_move, StationCargoList *dest, StationID next_station);
uint Return(uint max_move, StationCargoList *dest, StationID next_station, TileIndex current_tile);
uint Unload(uint max_move, StationCargoList *dest, CargoPayment *payment, TileIndex current_tile);
uint Shift(uint max_move, VehicleCargoList *dest);
uint Truncate(uint max_move = UINT_MAX);