diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index 22d0acd139..3b23fd4f58 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -16,6 +16,9 @@ #include "economy_base.h" #include "cargoaction.h" #include "order_type.h" +#include "company_func.h" +#include "core/backup_type.hpp" +#include "3rdparty/cpp-btree/btree_map.h" #include "safeguards.h" @@ -23,6 +26,51 @@ CargoPacketPool _cargopacket_pool("CargoPacket"); INSTANTIATE_POOL_METHODS(CargoPacket) +btree::btree_map _cargo_packet_deferred_payments; + +void ClearCargoPacketDeferredPayments() { + _cargo_packet_deferred_payments.clear(); +} + +void ChangeOwnershipOfCargoPacketDeferredPayments(Owner old_owner, Owner new_owner) +{ + std::vector> to_merge; + auto iter = _cargo_packet_deferred_payments.begin(); + while (iter != _cargo_packet_deferred_payments.end()) { + uint64 k = iter->first; + if ((CompanyID) GB(k, 24, 8) == old_owner) { + if (new_owner != INVALID_OWNER) { + SB(k, 24, 8, new_owner); + to_merge.push_back({ k, iter->second }); + } + iter = _cargo_packet_deferred_payments.erase(iter); + } else { + ++iter; + } + } + for (auto &m : to_merge) { + _cargo_packet_deferred_payments[m.first] += m.second; + } +} + +inline uint64 CargoPacketDeferredPaymentKey(CargoPacketID id, CompanyID cid, VehicleType type) +{ + return (((uint64) id) << 32) | (cid << 24) | (type << 22); +} + +template +inline void IterateCargoPacketDeferredPayments(CargoPacketID index, bool erase_range, F functor) +{ + auto start_iter = _cargo_packet_deferred_payments.lower_bound(CargoPacketDeferredPaymentKey(index, (CompanyID) 0, (VehicleType) 0)); + auto iter = start_iter; + for (; iter != _cargo_packet_deferred_payments.end() && iter->first >> 32 == index; ++iter) { + functor(iter->second, (CompanyID) GB(iter->first, 24, 8), (VehicleType) GB(iter->first, 22, 2)); + } + if (erase_range) { + _cargo_packet_deferred_payments.erase(start_iter, iter); + } +} + /** * Create a new packet for savegame loading. */ @@ -83,6 +131,16 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T this->source_type = source_type; } +/** Destroy the packet. */ +CargoPacket::~CargoPacket() +{ + if (CleaningPool()) return; + + if (this->flags & CPF_HAS_DEFERRED_PAYMENT) { + IterateCargoPacketDeferredPayments(this->index, true, [](Money &payment, CompanyID cid, VehicleType type) { }); + } +} + /** * Split this packet in two and return the split off part. * @param new_size Size of the split part. @@ -95,6 +153,20 @@ CargoPacket *CargoPacket::Split(uint new_size) Money fs = this->FeederShare(new_size); 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; + + if (this->flags & CPF_HAS_DEFERRED_PAYMENT) { + std::vector> to_add; + IterateCargoPacketDeferredPayments(this->index, false, [&](Money &payment, CompanyID cid, VehicleType type) { + Money share = payment * new_size / static_cast(this->count); + payment -= share; + to_add.push_back({ CargoPacketDeferredPaymentKey(cp_new->index, cid, type), share }); + }); + for (auto &m : to_add) { + _cargo_packet_deferred_payments[m.first] = m.second; + } + cp_new->flags |= CPF_HAS_DEFERRED_PAYMENT; + } + this->count -= new_size; return cp_new; } @@ -107,6 +179,20 @@ void CargoPacket::Merge(CargoPacket *cp) { this->count += cp->count; this->feeder_share += cp->feeder_share; + + if (cp->flags & CPF_HAS_DEFERRED_PAYMENT) { + std::vector> to_merge; + IterateCargoPacketDeferredPayments(cp->index, true, [&](Money &payment, CompanyID cid, VehicleType type) { + to_merge.push_back({ CargoPacketDeferredPaymentKey(this->index, cid, type), payment }); + }); + cp->flags &= ~CPF_HAS_DEFERRED_PAYMENT; + + for (auto &m : to_merge) { + _cargo_packet_deferred_payments[m.first] += m.second; + } + this->flags |= CPF_HAS_DEFERRED_PAYMENT; + } + delete cp; } @@ -118,9 +204,42 @@ void CargoPacket::Reduce(uint count) { assert(count < this->count); this->feeder_share -= this->FeederShare(count); + if (this->flags & CPF_HAS_DEFERRED_PAYMENT) { + IterateCargoPacketDeferredPayments(this->index, false, [&](Money &payment, CompanyID cid, VehicleType type) { + payment -= payment * count / static_cast(this->count); + }); + } this->count -= count; } +void CargoPacket::RegisterDeferredCargoPayment(CompanyID cid, VehicleType type, Money payment) +{ + this->flags |= CPF_HAS_DEFERRED_PAYMENT; + _cargo_packet_deferred_payments[CargoPacketDeferredPaymentKey(this->index, cid, type)] += payment; +} + +void CargoPacket::PayDeferredPayments() +{ + if (this->flags & CPF_HAS_DEFERRED_PAYMENT) { + IterateCargoPacketDeferredPayments(this->index, true, [&](Money &payment, CompanyID cid, VehicleType type) { + Backup cur_company(_current_company, cid, FILE_LINE); + + ExpensesType exp; + switch (type) { + case VEH_TRAIN: exp = EXPENSES_TRAIN_INC; break; + case VEH_ROAD: exp = EXPENSES_ROADVEH_INC; break; + case VEH_SHIP: exp = EXPENSES_SHIP_INC; break; + case VEH_AIRCRAFT: exp = EXPENSES_AIRCRAFT_INC; break; + default: NOT_REACHED(); + } + SubtractMoneyFromCompany(CommandCost(exp, -payment)); + + cur_company.Restore(); + }); + this->flags &= ~CPF_HAS_DEFERRED_PAYMENT; + } +} + /** * Invalidates (sets source_id to INVALID_SOURCE) all cargo packets from given source. * @param src_type Type of source. diff --git a/src/cargopacket.h b/src/cargopacket.h index 0ed4fd9bbc..16cfd13104 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -18,6 +18,7 @@ #include "order_type.h" #include "cargo_type.h" #include "vehicle_type.h" +#include "company_type.h" #include "core/multimap.hpp" #include @@ -38,6 +39,9 @@ extern const struct SaveLoad *GetCargoPacketDesc(); typedef uint32 TileOrStationID; +void ClearCargoPacketDeferredPayments(); +void ChangeOwnershipOfCargoPacketDeferredPayments(Owner old_owner, Owner new_owner); + /** * Container for cargo from the same location and time. */ @@ -54,6 +58,12 @@ private: TileOrStationID loaded_at_xy; ///< Location where this cargo has been loaded into the vehicle. TileOrStationID next_station; ///< Station where the cargo wants to go next. }; + uint flags = 0; ///< NOSAVE: temporary flags + + /** Vehicle status bits in #Vehicle::vehstatus. */ + enum CargoPacketFlags { + CPF_HAS_DEFERRED_PAYMENT = 0x01, ///< Cargo packet has 1 or more deferred payment(s) + }; /** The CargoList caches, thus needs to know about it. */ template friend class CargoList; @@ -61,6 +71,7 @@ private: friend class StationCargoList; /** We want this to be saved, right? */ friend const struct SaveLoad *GetCargoPacketDesc(); + friend void Load_CPDP(); public: /** Maximum number of items in a single cargo packet. */ static const uint16 MAX_COUNT = UINT16_MAX; @@ -68,9 +79,7 @@ public: CargoPacket(); CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id); CargoPacket(uint16 count, byte days_in_transit, StationID source, TileIndex source_xy, TileIndex loaded_at_xy, Money feeder_share = 0, SourceType source_type = ST_INDUSTRY, SourceID source_id = INVALID_SOURCE); - - /** Destroy the packet. */ - ~CargoPacket() { } + ~CargoPacket(); CargoPacket *Split(uint new_size); void Merge(CargoPacket *cp); @@ -124,6 +133,9 @@ public: return this->feeder_share * part / static_cast(this->count); } + void RegisterDeferredCargoPayment(CompanyID cid, VehicleType type, Money payment); + void PayDeferredPayments(); + /** * Gets the number of days this cargo has been in transit. * This number isn't really in days, but in 2.5 days (CARGO_AGING_TICKS = 185 ticks) and diff --git a/src/economy.cpp b/src/economy.cpp index 86065a1161..02f4f29195 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -563,6 +563,9 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner) /* Change colour of existing windows */ if (new_owner != INVALID_OWNER) ChangeWindowOwner(old_owner, new_owner); + /* Change owner of deferred cargo payments */ + ChangeOwnershipOfCargoPacketDeferredPayments(old_owner, new_owner); + cur_company.Restore(); MarkWholeScreenDirty(); @@ -1209,7 +1212,7 @@ CargoPayment::~CargoPayment() * @param cp The cargo packet to pay for. * @param count The number of packets to pay for. */ -void CargoPayment::PayFinalDelivery(const CargoPacket *cp, uint count) +void CargoPayment::PayFinalDelivery(CargoPacket *cp, uint count) { if (this->owner == NULL) { this->owner = Company::Get(this->front->owner); @@ -1218,11 +1221,14 @@ void CargoPayment::PayFinalDelivery(const CargoPacket *cp, uint count) /* Handle end of route payment */ Money profit = DeliverGoods(count, this->ct, this->current_station, cp->SourceStationXY(), cp->DaysInTransit(), this->owner, cp->SourceSubsidyType(), cp->SourceSubsidyID()); + profit -= cp->FeederShare(count); + /* For Infrastructure patch. Handling transfers between other companies */ - this->route_profit += profit - cp->FeederShare(count); + this->route_profit += profit; + cp->PayDeferredPayments(); /* The vehicle's profit is whatever route profit there is minus feeder shares. */ - this->visual_profit += profit - cp->FeederShare(count); + this->visual_profit += profit; } /** @@ -1231,7 +1237,7 @@ void CargoPayment::PayFinalDelivery(const CargoPacket *cp, uint count) * @param count The number of packets to pay for. * @return The amount of money paid for the transfer. */ -Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count) +Money CargoPayment::PayTransfer(CargoPacket *cp, uint count) { Money profit = GetTransportedGoodsIncome( count, @@ -1243,7 +1249,7 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count) profit = profit * _settings_game.economy.feeder_payment_share / 100; /* For Infrastructure patch. Handling transfers between other companies */ - this->route_profit += profit; + cp->RegisterDeferredCargoPayment(this->front->owner, this->front->type, profit); this->visual_transfer += profit; // accumulate transfer profits for whole vehicle return profit; // account for the (virtual) profit already made for the cargo packet diff --git a/src/economy_base.h b/src/economy_base.h index 60b0964a95..f958aff4ed 100644 --- a/src/economy_base.h +++ b/src/economy_base.h @@ -39,8 +39,8 @@ struct CargoPayment : CargoPaymentPool::PoolItem<&_cargo_payment_pool> { CargoPayment(Vehicle *front); ~CargoPayment(); - Money PayTransfer(const CargoPacket *cp, uint count); - void PayFinalDelivery(const CargoPacket *cp, uint count); + Money PayTransfer(CargoPacket *cp, uint count); + void PayFinalDelivery(CargoPacket *cp, uint count); /** * Sets the currently handled cargo type. diff --git a/src/misc.cpp b/src/misc.cpp index d9d506993f..94f491df0b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -28,6 +28,7 @@ #include "core/pool_type.hpp" #include "game/game.hpp" #include "linkgraph/linkgraphschedule.h" +#include "cargopacket.h" #include "safeguards.h" @@ -72,6 +73,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin } LinkGraphSchedule::Clear(); + ClearCargoPacketDeferredPayments(); PoolBase::Clean(PT_NORMAL); ResetPersistentNewGRFData(); diff --git a/src/openttd.cpp b/src/openttd.cpp index ba4a95e428..39d867528a 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -63,6 +63,7 @@ #include "subsidy_func.h" #include "gfx_layout.h" #include "viewport_sprite_sorter.h" +#include "cargopacket.h" #include "linkgraph/linkgraphschedule.h" @@ -334,6 +335,7 @@ static void ShutdownGame() #endif LinkGraphSchedule::Clear(); + ClearCargoPacketDeferredPayments(); PoolBase::Clean(PT_ALL); /* No NewGRFs were loaded when it was still bootstrapping. */ diff --git a/src/saveload/cargopacket_sl.cpp b/src/saveload/cargopacket_sl.cpp index dc8d4145e3..9f5ba6d260 100644 --- a/src/saveload/cargopacket_sl.cpp +++ b/src/saveload/cargopacket_sl.cpp @@ -12,11 +12,14 @@ #include "../stdafx.h" #include "../vehicle_base.h" #include "../station_base.h" +#include "../3rdparty/cpp-btree/btree_map.h" #include "saveload.h" #include "../safeguards.h" +extern btree::btree_map _cargo_packet_deferred_payments; + /** * Savegame conversion for cargopackets. */ @@ -137,7 +140,42 @@ static void Load_CAPA() } } +/** + * Save cargo packet deferred payments. + */ +void Save_CPDP() +{ + SlSetLength(16 * _cargo_packet_deferred_payments.size()); + + for (auto &it : _cargo_packet_deferred_payments) { + SlWriteUint64(it.first); + SlWriteUint64(it.second); + } +} + +/** + * Load cargo packet deferred payments. + */ +void Load_CPDP() +{ + uint count = SlGetFieldLength() / 16; + uint last_cargo_packet_id = (uint) -1; + + for (uint i = 0; i < count; i++) { + uint64 k = SlReadUint64(); + uint64 v = SlReadUint64(); + _cargo_packet_deferred_payments[k] = v; + if (k >> 32 != last_cargo_packet_id) { + last_cargo_packet_id = k >> 32; + CargoPacket::Get(last_cargo_packet_id)->flags |= CargoPacket::CPF_HAS_DEFERRED_PAYMENT; + } + } +} + + + /** Chunk handlers related to cargo packets. */ extern const ChunkHandler _cargopacket_chunk_handlers[] = { - { 'CAPA', Save_CAPA, Load_CAPA, NULL, NULL, CH_ARRAY | CH_LAST}, + { 'CAPA', Save_CAPA, Load_CAPA, NULL, NULL, CH_ARRAY }, + { 'CPDP', Save_CPDP, Load_CPDP, NULL, NULL, CH_RIFF | CH_LAST }, }; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 2aca9cbc87..524e275c65 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,7 +45,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { - { XSLFI_INFRA_SHARING, XSCF_NULL, 1, 1, "infra_sharing", NULL, NULL, NULL }, + { XSLFI_INFRA_SHARING, XSCF_NULL, 2, 2, "infra_sharing", NULL, NULL, "CPDP" }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker };