(svn r16694) -Fix [FS#2995] (rgradual loading, rnewindustries): only pay for whatever has been actually unloaded and perform the payment when unloading has finished. This fixes, amongst others:

* cheating by starting to unload and after getting paid rushing to the depot to get sold (or unloading, loading and getting paid again for the remainder)
 * cargo being dropped onto a station at the moment a stockpiling industry doesn't accept it anymore
 * industries getting cargo that has not been unloaded yet and subsequently dumping it back on the station in one go
Note: you will now get paid after the unloading has finished, so you'll have to wait a bit longer for 'your' money.
pull/155/head
rubidium 15 years ago
parent 025cf4b546
commit 4f99508114

@ -111,7 +111,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()); uint amount = min(src->cargo.Count(), dest->cargo_cap - dest->cargo.Count());
if (amount <= 0) continue; if (amount <= 0) continue;
src->cargo.MoveTo(&dest->cargo, amount); src->cargo.MoveTo(&dest->cargo, amount, CargoList::MTA_UNLOAD, NULL);
} }
} }

@ -5,6 +5,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "station_base.h" #include "station_base.h"
#include "core/pool_func.hpp" #include "core/pool_func.hpp"
#include "economy_base.h"
/* Initialize the cargopacket-pool */ /* Initialize the cargopacket-pool */
CargoPacketPool _cargopacket_pool("CargoPacket"); CargoPacketPool _cargopacket_pool("CargoPacket");
@ -26,7 +27,6 @@ CargoPacket::CargoPacket(StationID source, uint16 count)
this->count = count; this->count = count;
this->days_in_transit = 0; this->days_in_transit = 0;
this->feeder_share = 0; this->feeder_share = 0;
this->paid_for = false;
} }
/* /*
@ -99,9 +99,10 @@ void CargoList::Truncate(uint count)
InvalidateCache(); InvalidateCache();
} }
bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, uint data) bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, CargoPayment *payment, uint data)
{ {
assert(mta == MTA_FINAL_DELIVERY || dest != NULL); assert(mta == MTA_FINAL_DELIVERY || dest != NULL);
assert(mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL);
CargoList tmp; CargoList tmp;
while (!packets.empty() && count > 0) { while (!packets.empty() && count > 0) {
@ -114,20 +115,25 @@ bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta,
if (cp->source == data) { if (cp->source == data) {
tmp.Append(cp); tmp.Append(cp);
} else { } else {
payment->PayFinalDelivery(cp, cp->count);
count -= cp->count; count -= cp->count;
delete cp; delete cp;
} }
break; continue; // of the loop
case MTA_CARGO_LOAD: case MTA_CARGO_LOAD:
cp->loaded_at_xy = data; cp->loaded_at_xy = data;
/* When cargo is moved into another vehicle you have *always* paid for it */ break;
cp->paid_for = false;
/* FALL THROUGH */ case MTA_TRANSFER:
case MTA_OTHER: payment->PayTransfer(cp, cp->count);
count -= cp->count; break;
dest->packets.push_back(cp);
case MTA_UNLOAD:
break; break;
} }
count -= cp->count;
dest->packets.push_back(cp);
} else { } else {
/* Can move only part of the packet, so split it into two pieces */ /* Can move only part of the packet, so split it into two pieces */
if (mta != MTA_FINAL_DELIVERY) { if (mta != MTA_FINAL_DELIVERY) {
@ -142,11 +148,13 @@ bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta,
cp_new->days_in_transit = cp->days_in_transit; cp_new->days_in_transit = cp->days_in_transit;
cp_new->feeder_share = fs; cp_new->feeder_share = fs;
/* When cargo is moved into another vehicle you have *always* paid for it */
cp_new->paid_for = (mta == MTA_CARGO_LOAD) ? false : cp->paid_for;
cp_new->count = count; cp_new->count = count;
dest->packets.push_back(cp_new); dest->packets.push_back(cp_new);
if (mta == MTA_TRANSFER) payment->PayTransfer(cp_new, count);
} else {
payment->PayFinalDelivery(cp, count);
} }
cp->count -= count; cp->count -= count;
@ -158,7 +166,7 @@ bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta,
if (mta == MTA_FINAL_DELIVERY && !tmp.Empty()) { if (mta == MTA_FINAL_DELIVERY && !tmp.Empty()) {
/* There are some packets that could not be delivered at the station, put them back */ /* There are some packets that could not be delivered at the station, put them back */
tmp.MoveTo(this, UINT_MAX); tmp.MoveTo(this, UINT_MAX, MTA_UNLOAD, NULL);
tmp.packets.clear(); tmp.packets.clear();
} }
@ -172,7 +180,6 @@ void CargoList::InvalidateCache()
{ {
empty = packets.empty(); empty = packets.empty();
count = 0; count = 0;
unpaid_cargo = false;
feeder_share = 0; feeder_share = 0;
source = INVALID_STATION; source = INVALID_STATION;
days_in_transit = 0; days_in_transit = 0;
@ -182,7 +189,6 @@ void CargoList::InvalidateCache()
uint dit = 0; uint dit = 0;
for (List::const_iterator it = packets.begin(); it != packets.end(); it++) { for (List::const_iterator it = packets.begin(); it != packets.end(); it++) {
count += (*it)->count; count += (*it)->count;
unpaid_cargo |= !(*it)->paid_for;
dit += (*it)->days_in_transit * (*it)->count; dit += (*it)->days_in_transit * (*it)->count;
feeder_share += (*it)->feeder_share; feeder_share += (*it)->feeder_share;
} }

@ -29,7 +29,6 @@ struct CargoPacket : CargoPacketPool::PoolItem<&_cargopacket_pool> {
uint16 count; ///< The amount of cargo in this packet uint16 count; ///< The amount of cargo in this packet
byte days_in_transit; ///< Amount of days this packet has been in transit byte days_in_transit; ///< Amount of days this packet has been in transit
bool paid_for; ///< Have we been paid for this cargo packet?
/** /**
* Creates a new cargo packet * Creates a new cargo packet
@ -50,7 +49,7 @@ struct CargoPacket : CargoPacketPool::PoolItem<&_cargopacket_pool> {
*/ */
FORCEINLINE bool SameSource(const CargoPacket *cp) const FORCEINLINE bool SameSource(const CargoPacket *cp) const
{ {
return this->source_xy == cp->source_xy && this->days_in_transit == cp->days_in_transit && this->paid_for == cp->paid_for; return this->source_xy == cp->source_xy && this->days_in_transit == cp->days_in_transit;
} }
}; };
@ -81,7 +80,8 @@ public:
enum MoveToAction { enum MoveToAction {
MTA_FINAL_DELIVERY, ///< "Deliver" the packet to the final destination, i.e. destroy the packet 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_CARGO_LOAD, ///< Load the packet onto a vehicle, i.e. set the last loaded station ID
MTA_OTHER ///< "Just" move the packet to another cargo list MTA_TRANSFER, ///< The cargo is moved as part of a transfer
MTA_UNLOAD, ///< The cargo is moved as part of a forced unload
}; };
private: private:
@ -89,7 +89,6 @@ private:
bool empty; ///< Cache for whether this list is empty or not bool empty; ///< Cache for whether this list is empty or not
uint count; ///< Cache for the number of cargo entities uint count; ///< Cache for the number of cargo entities
bool unpaid_cargo; ///< Cache for the unpaid cargo
Money feeder_share; ///< Cache for the feeder share Money feeder_share; ///< Cache for the feeder share
StationID source; ///< Cache for the source of the packet StationID source; ///< Cache for the source of the packet
uint days_in_transit; ///< Cache for the number of days in transit uint days_in_transit; ///< Cache for the number of days in transit
@ -125,12 +124,6 @@ public:
*/ */
FORCEINLINE uint Count() const { return this->count; } FORCEINLINE uint Count() const { return this->count; }
/**
* Is there some cargo that has not been paid for?
* @return true if and only if there is such a cargo
*/
FORCEINLINE bool UnpaidCargo() const { return this->unpaid_cargo; }
/** /**
* Returns total sum of the feeder share for all packets * Returns total sum of the feeder share for all packets
* @return the before mentioned number * @return the before mentioned number
@ -171,18 +164,23 @@ public:
* Depending on the value of mta the side effects of this function differ: * 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_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_CARGO_LOAD: sets the loaded_at_xy value of the moved packets
* - MTA_OTHER: just move without side effects * - MTA_TRANSFER: just move without side effects
* - MTA_UNLOAD: just move without side effects
* @param dest the destination to move the cargo to * @param dest the destination to move the cargo to
* @param count the amount of cargo entities to move * @param count the amount of cargo entities to move
* @param mta how to handle the moving (side effects) * @param mta how to handle the moving (side effects)
* @param data Depending on mta the data of this variable differs: * @param data Depending on mta the data of this variable differs:
* - MTA_FINAL_DELIVERY - station ID of packet's origin not to remove * - MTA_FINAL_DELIVERY - station ID of packet's origin not to remove
* - MTA_CARGO_LOAD - station's tile index of load * - MTA_CARGO_LOAD - station's tile index of load
* - MTA_OTHER - unused * - MTA_TRANSFER - unused
* @param mta == MTA_FINAL_DELIVERY || dest != NULL * - 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 * @return true if there are still packets that might be moved from this cargo list
*/ */
bool MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta = MTA_OTHER, uint data = 0); bool MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, CargoPayment *payment, uint data = 0);
/** Invalidates the cached data and rebuild it */ /** Invalidates the cached data and rebuild it */
void InvalidateCache(); void InvalidateCache();

@ -36,10 +36,16 @@
#include "subsidy_func.h" #include "subsidy_func.h"
#include "station_base.h" #include "station_base.h"
#include "economy_base.h" #include "economy_base.h"
#include "core/pool_func.hpp"
#include "table/strings.h" #include "table/strings.h"
#include "table/sprites.h" #include "table/sprites.h"
/* Initialize the cargo payment-pool */
CargoPaymentPool _cargo_payment_pool("CargoPayment");
INSTANTIATE_POOL_METHODS(CargoPayment)
/** /**
* Multiply two integer values and shift the results to right. * Multiply two integer values and shift the results to right.
* *
@ -1148,15 +1154,16 @@ static void TriggerIndustryProduction(Industry *i)
*/ */
CargoPayment::CargoPayment(Vehicle *front) : CargoPayment::CargoPayment(Vehicle *front) :
front(front), front(front),
route_profit(0),
visual_profit(0),
owner(NULL),
current_station(front->last_station_visited) current_station(front->last_station_visited)
{ {
} }
CargoPayment::~CargoPayment() CargoPayment::~CargoPayment()
{ {
if (this->CleaningPool()) return;
this->front->cargo_payment = NULL;
if (this->visual_profit == 0) return; if (this->visual_profit == 0) return;
CompanyID old_company = _current_company; CompanyID old_company = _current_company;
@ -1195,8 +1202,6 @@ void CargoPayment::PayFinalDelivery(CargoPacket *cp, uint count)
/* The vehicle's profit is whatever route profit there is minus feeder shares. */ /* The vehicle's profit is whatever route profit there is minus feeder shares. */
this->visual_profit += profit - cp->feeder_share; this->visual_profit += profit - cp->feeder_share;
cp->paid_for = true;
} }
/** /**
@ -1215,80 +1220,30 @@ void CargoPayment::PayTransfer(CargoPacket *cp, uint count)
this->visual_profit += profit; // accumulate transfer profits for whole vehicle this->visual_profit += profit; // accumulate transfer profits for whole vehicle
cp->feeder_share += profit; // account for the (virtual) profit already made for the cargo packet cp->feeder_share += profit; // account for the (virtual) profit already made for the cargo packet
cp->paid_for = true;
} }
/** /**
* Performs the vehicle payment _and_ marks the vehicle to be unloaded. * Prepare the vehicle to be unloaded.
* @param front_v the vehicle to be unloaded * @param front_v the vehicle to be unloaded
*/ */
void VehiclePayment(Vehicle *front_v) void PrepareUnload(Vehicle *front_v)
{ {
int result = 0;
Money vehicle_profit = 0; // Money paid to the train
Money route_profit = 0; // The grand total amount for the route. A-D of transfer chain A-B-C-D
Money virtual_profit = 0; // The virtual profit for entire vehicle chain
StationID last_visited = front_v->last_station_visited;
Station *st = Station::Get(last_visited);
/* At this moment loading cannot be finished */ /* At this moment loading cannot be finished */
ClrBit(front_v->vehicle_flags, VF_LOADING_FINISHED); ClrBit(front_v->vehicle_flags, VF_LOADING_FINISHED);
/* Start unloading in at the first possible moment */ /* Start unloading in at the first possible moment */
front_v->load_unload_time_rem = 1; front_v->load_unload_time_rem = 1;
CargoPayment payment(front_v); if ((front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
for (Vehicle *v = front_v; v != NULL; v = v->Next()) {
for (Vehicle *v = front_v; v != NULL; v = v->Next()) { if (v->cargo_cap > 0 && !v->cargo.Empty()) {
/* No cargo to unload */
if (v->cargo_cap == 0 || v->cargo.Empty() || (front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD)) continue;
/* All cargo has already been paid for, no need to pay again */
if (!v->cargo.UnpaidCargo()) {
SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
continue;
}
GoodsEntry *ge = &st->goods[v->cargo_type];
const CargoList::List *cargos = v->cargo.Packets();
payment.SetCargo(v->cargo_type);
for (CargoList::List::const_iterator it = cargos->begin(); it != cargos->end(); it++) {
CargoPacket *cp = *it;
if (!cp->paid_for &&
cp->source != last_visited &&
HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) &&
(front_v->current_order.GetUnloadType() & OUFB_TRANSFER) == 0) {
/* Deliver goods to the station */
st->time_since_unload = 0;
payment.PayFinalDelivery(cp, cp->count);
result |= 1;
SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
} else if (front_v->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) {
if (!cp->paid_for && (front_v->current_order.GetUnloadType() & OUFB_TRANSFER) != 0) {
payment.PayTransfer(cp, cp->count);
}
result |= 2;
SetBit(v->vehicle_flags, VF_CARGO_UNLOADING); SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
} }
} }
v->cargo.InvalidateCache();
} }
/* Call the production machinery of industries */ assert(front_v->cargo_payment == NULL);
const Industry * const *isend = _cargo_delivery_destinations.End(); front_v->cargo_payment = new CargoPayment(front_v);
for (Industry **iid = _cargo_delivery_destinations.Begin(); iid != isend; iid++) {
TriggerIndustryProduction(*iid);
}
_cargo_delivery_destinations.Clear();
} }
/** /**
@ -1337,6 +1292,8 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
v->cur_speed = 0; v->cur_speed = 0;
CargoPayment *payment = v->cargo_payment;
for (; v != NULL; v = v->Next()) { for (; v != NULL; v = v->Next()) {
if (v->cargo_cap == 0) continue; if (v->cargo_cap == 0) continue;
@ -1358,9 +1315,11 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
bool remaining = false; // Are there cargo entities in this vehicle that can still be unloaded here? 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? bool accepted = false; // Is the cargo accepted by the station?
payment->SetCargo(v->cargo_type);
if (HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) && !(u->current_order.GetUnloadType() & OUFB_TRANSFER)) { if (HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) && !(u->current_order.GetUnloadType() & OUFB_TRANSFER)) {
/* The cargo has reached it's final destination, the packets may now be destroyed */ /* The cargo has reached it's final destination, the packets may now be destroyed */
remaining = v->cargo.MoveTo(NULL, amount_unloaded, CargoList::MTA_FINAL_DELIVERY, last_visited); remaining = v->cargo.MoveTo(NULL, amount_unloaded, CargoList::MTA_FINAL_DELIVERY, payment, last_visited);
result |= 1; result |= 1;
accepted = true; accepted = true;
@ -1372,7 +1331,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
* station is still accepting the cargo in the vehicle. It doesn't * station is still accepting the cargo in the vehicle. It doesn't
* accept cargo that was loaded at the same station. */ * accept cargo that was loaded at the same station. */
if ((u->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) && (!accepted || v->cargo.Count() == cargo_count)) { if ((u->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) && (!accepted || v->cargo.Count() == cargo_count)) {
remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded); remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded, u->current_order.GetUnloadType() & OUFB_TRANSFER ? CargoList::MTA_TRANSFER : CargoList::MTA_UNLOAD, payment);
SetBit(ge->acceptance_pickup, GoodsEntry::PICKUP); SetBit(ge->acceptance_pickup, GoodsEntry::PICKUP);
result |= 2; result |= 2;
@ -1449,7 +1408,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
completely_emptied = false; completely_emptied = false;
anything_loaded = true; anything_loaded = true;
ge->cargo.MoveTo(&v->cargo, cap, CargoList::MTA_CARGO_LOAD, st->xy); ge->cargo.MoveTo(&v->cargo, cap, CargoList::MTA_CARGO_LOAD, NULL, st->xy);
st->time_since_load = 0; st->time_since_load = 0;
st->last_vehicle_type = v->type; st->last_vehicle_type = v->type;
@ -1485,6 +1444,8 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
v = u; v = u;
if (!anything_unloaded) delete payment;
if (anything_loaded || anything_unloaded) { if (anything_loaded || anything_unloaded) {
if (_settings_game.order.gradual_loading) { if (_settings_game.order.gradual_loading) {
/* The time it takes to load one 'slice' of cargo or passengers depends /* The time it takes to load one 'slice' of cargo or passengers depends
@ -1573,6 +1534,13 @@ void LoadUnloadStation(Station *st)
Vehicle *v = *iter; Vehicle *v = *iter;
if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left); if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left);
} }
/* Call the production machinery of industries */
const Industry * const *isend = _cargo_delivery_destinations.End();
for (Industry **iid = _cargo_delivery_destinations.Begin(); iid != isend; iid++) {
TriggerIndustryProduction(*iid);
}
_cargo_delivery_destinations.Clear();
} }
void CompaniesMonthlyLoop() void CompaniesMonthlyLoop()

@ -7,19 +7,26 @@
#include "cargopacket.h" #include "cargopacket.h"
/** Type of pool to store cargo payments in. */
typedef Pool<CargoPayment, CargoPaymentID, 512, 64000> CargoPaymentPool;
/** The actual pool to store cargo payments in. */
extern CargoPaymentPool _cargo_payment_pool;
/** /**
* Helper class to perform the cargo payment. * Helper class to perform the cargo payment.
*/ */
struct CargoPayment { struct CargoPayment : CargoPaymentPool::PoolItem<&_cargo_payment_pool> {
Vehicle *front; ///< The front vehicle to do the payment of Vehicle *front; ///< The front vehicle to do the payment of
Money route_profit; ///< The amount of money to add/remove from the bank account Money route_profit; ///< The amount of money to add/remove from the bank account
Money visual_profit; ///< The visual profit to show Money visual_profit; ///< The visual profit to show
/* Unsaved variables */
Company *owner; ///< The owner of the vehicle Company *owner; ///< The owner of the vehicle
StationID current_station; ///< The current station StationID current_station; ///< The current station
CargoID ct; ///< The currently handled cargo type CargoID ct; ///< The currently handled cargo type
/** Constructor for pool saveload */ /** Constructor for pool saveload */
CargoPayment() {}
CargoPayment(Vehicle *front); CargoPayment(Vehicle *front);
~CargoPayment(); ~CargoPayment();
@ -33,4 +40,7 @@ struct CargoPayment {
void SetCargo(CargoID ct) { this->ct = ct; } void SetCargo(CargoID ct) { this->ct = ct; }
}; };
#define FOR_ALL_CARGO_PAYMENTS_FROM(var, start) FOR_ALL_ITEMS_FROM(CargoPayment, cargo_payment_index, var, start)
#define FOR_ALL_CARGO_PAYMENTS(var) FOR_ALL_CARGO_PAYMENTS_FROM(var, 0)
#endif /* ECONOMY_BASE_H */ #endif /* ECONOMY_BASE_H */

@ -34,7 +34,7 @@ void StartupIndustryDailyChanges(bool init_counter);
Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type); Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type);
uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount); uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount);
void VehiclePayment(Vehicle *front_v); void PrepareUnload(Vehicle *front_v);
void LoadUnloadStation(Station *st); void LoadUnloadStation(Station *st);
Money GetPriceByIndex(uint8 index); Money GetPriceByIndex(uint8 index);

@ -125,4 +125,7 @@ enum ExpensesType {
/** The "steps" in loan size, in British Pounds! */ /** The "steps" in loan size, in British Pounds! */
static const int LOAN_INTERVAL = 10000; static const int LOAN_INTERVAL = 10000;
struct CargoPayment;
typedef uint32 CargoPaymentID;
#endif /* ECONOMY_TYPE_H */ #endif /* ECONOMY_TYPE_H */

@ -29,6 +29,7 @@
#include "../road_cmd.h" #include "../road_cmd.h"
#include "../ai/ai.hpp" #include "../ai/ai.hpp"
#include "../town.h" #include "../town.h"
#include "../economy_base.h"
#include "table/strings.h" #include "table/strings.h"
@ -547,6 +548,13 @@ bool AfterLoadGame()
if (!Company::IsValidID(COMPANY_FIRST) && (!_networking || (_networking && _network_server && !_network_dedicated))) if (!Company::IsValidID(COMPANY_FIRST) && (!_networking || (_networking && _network_server && !_network_dedicated)))
DoStartupNewCompany(false); DoStartupNewCompany(false);
/* Fix the cache for cargo payments. */
CargoPayment *cp;
FOR_ALL_CARGO_PAYMENTS(cp) {
cp->front->cargo_payment = cp;
cp->current_station = cp->front->last_station_visited;
}
if (CheckSavegameVersion(72)) { if (CheckSavegameVersion(72)) {
/* Locks/shiplifts in very old savegames had OWNER_WATER as owner */ /* Locks/shiplifts in very old savegames had OWNER_WATER as owner */
for (TileIndex t = 0; t < MapSize(); t++) { for (TileIndex t = 0; t < MapSize(); t++) {
@ -1269,13 +1277,8 @@ bool AfterLoadGame()
* stored to stop people cheating and cashing in several times. This * stored to stop people cheating and cashing in several times. This
* wasn't enough though as it was cleared when the vehicle started * wasn't enough though as it was cleared when the vehicle started
* loading again, even if it didn't actually load anything, so now the * loading again, even if it didn't actually load anything, so now the
* amount of cargo that has been paid for is stored. */ * amount that has been paid is stored. */
FOR_ALL_VEHICLES(v) { FOR_ALL_VEHICLES(v) {
const CargoList::List *packets = v->cargo.Packets();
for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
CargoPacket *cp = *it;
cp->paid_for = HasBit(v->vehicle_flags, 2);
}
ClrBit(v->vehicle_flags, 2); ClrBit(v->vehicle_flags, 2);
v->cargo.InvalidateCache(); v->cargo.InvalidateCache();
} }
@ -1870,6 +1873,21 @@ bool AfterLoadGame()
} }
} }
} }
/* We didn't store cargo payment yet, so make them for vehicles that are
* currently at a station and loading/unloading. If they don't get any
* payment anymore they just removed in the next load/unload cycle.
* However, some 0.7 versions might have cargo payment. For those we just
* add cargopayment for the vehicles that don't have it.
*/
Station *st;
FOR_ALL_STATIONS(st) {
std::list<Vehicle *>::iterator iter;
for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
Vehicle *v = *iter;
if (v->cargo_payment == NULL) v->cargo_payment = new CargoPayment(v);
}
}
} }
AfterLoadLabelMaps(); AfterLoadLabelMaps();

@ -8,13 +8,15 @@
#include "saveload.h" #include "saveload.h"
static const SaveLoad _cargopacket_desc[] = { static const SaveLoad _cargopacket_desc[] = {
SLE_VAR(CargoPacket, source, SLE_UINT16), SLE_VAR(CargoPacket, source, SLE_UINT16),
SLE_VAR(CargoPacket, source_xy, SLE_UINT32), SLE_VAR(CargoPacket, source_xy, SLE_UINT32),
SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32), SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32),
SLE_VAR(CargoPacket, count, SLE_UINT16), SLE_VAR(CargoPacket, count, SLE_UINT16),
SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8), SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8),
SLE_VAR(CargoPacket, feeder_share, SLE_INT64), SLE_VAR(CargoPacket, feeder_share, SLE_INT64),
SLE_VAR(CargoPacket, paid_for, SLE_BOOL),
/* Used to be paid_for, but that got changed. */
SLE_CONDNULL(1, 0, 120),
SLE_END() SLE_END()
}; };

@ -4,6 +4,7 @@
#include "../stdafx.h" #include "../stdafx.h"
#include "../economy_func.h" #include "../economy_func.h"
#include "../economy_base.h"
#include "saveload.h" #include "saveload.h"
@ -51,8 +52,45 @@ static void Load_ECMY()
StartupIndustryDailyChanges(CheckSavegameVersion(102)); // old savegames will need to be initialized StartupIndustryDailyChanges(CheckSavegameVersion(102)); // old savegames will need to be initialized
} }
static const SaveLoad _cargopayment_desc[] = {
SLE_REF(CargoPayment, front, REF_VEHICLE),
SLE_VAR(CargoPayment, route_profit, SLE_INT64),
SLE_VAR(CargoPayment, visual_profit, SLE_INT64),
SLE_END()
};
static void Save_CAPY()
{
CargoPayment *cp;
FOR_ALL_CARGO_PAYMENTS(cp) {
SlSetArrayIndex(cp->index);
SlObject(cp, _cargopayment_desc);
}
}
static void Load_CAPY()
{
int index;
while ((index = SlIterateArray()) != -1) {
CargoPayment *cp = new (index) CargoPayment();
SlObject(cp, _cargopayment_desc);
}
}
static void Ptrs_CAPY()
{
CargoPayment *cp;
FOR_ALL_CARGO_PAYMENTS(cp) {
SlObject(cp, _cargopayment_desc);
}
}
extern const ChunkHandler _economy_chunk_handlers[] = { extern const ChunkHandler _economy_chunk_handlers[] = {
{ 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, NULL, CH_RIFF | CH_AUTO_LENGTH}, { 'CAPY', Save_CAPY, Load_CAPY, Ptrs_CAPY, CH_ARRAY},
{ 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, NULL, CH_RIFF | CH_AUTO_LENGTH}, { 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, NULL, CH_RIFF | CH_AUTO_LENGTH},
{ 'ECMY', Save_ECMY, Load_ECMY, NULL, CH_RIFF | CH_LAST}, { 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, NULL, CH_RIFF | CH_AUTO_LENGTH},
{ 'ECMY', Save_ECMY, Load_ECMY, NULL, CH_RIFF | CH_LAST},
}; };

@ -41,7 +41,7 @@
#include "saveload_internal.h" #include "saveload_internal.h"
extern const uint16 SAVEGAME_VERSION = 120; extern const uint16 SAVEGAME_VERSION = 121;
SavegameType _savegame_type; ///< type of savegame we are loading SavegameType _savegame_type; ///< type of savegame we are loading

@ -38,6 +38,7 @@
#include "settings_type.h" #include "settings_type.h"
#include "network/network.h" #include "network/network.h"
#include "core/pool_func.hpp" #include "core/pool_func.hpp"
#include "economy_base.h"
#include "table/sprites.h" #include "table/sprites.h"
#include "table/strings.h" #include "table/strings.h"
@ -446,6 +447,7 @@ static AutoreplaceMap _vehicles_to_autoreplace;
void InitializeVehicles() void InitializeVehicles()
{ {
_vehicle_pool.CleanPool(); _vehicle_pool.CleanPool();
_cargo_payment_pool.CleanPool();
_vehicles_to_autoreplace.Reset(); _vehicles_to_autoreplace.Reset();
ResetVehiclePosHash(); ResetVehiclePosHash();
@ -1442,7 +1444,7 @@ void Vehicle::BeginLoading()
Station::Get(this->last_station_visited)->loading_vehicles.push_back(this); Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
VehiclePayment(this); PrepareUnload(this);
InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner); InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH); InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
@ -1458,6 +1460,8 @@ void Vehicle::LeaveStation()
{ {
assert(current_order.IsType(OT_LOADING)); assert(current_order.IsType(OT_LOADING));
delete this->cargo_payment;
/* Only update the timetable if the vehicle was supposed to stop here. */ /* Only update the timetable if the vehicle was supposed to stop here. */
if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false); if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);

@ -91,7 +91,9 @@ public:
Money profit_this_year; ///< Profit this year << 8, low 8 bits are fract Money profit_this_year; ///< Profit this year << 8, low 8 bits are fract
Money profit_last_year; ///< Profit last year << 8, low 8 bits are fract Money profit_last_year; ///< Profit last year << 8, low 8 bits are fract
Money value; Money value; ///< Value of the vehicle
CargoPayment *cargo_payment; ///< The cargo payment we're currently in
/* Used for timetabling. */ /* Used for timetabling. */
uint32 current_order_time; ///< How many ticks have passed since this order started. uint32 current_order_time; ///< How many ticks have passed since this order started.

Loading…
Cancel
Save