diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index f1bbfaa916..09bd78f030 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 @@ -25,6 +28,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. */ @@ -85,6 +133,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. @@ -97,6 +155,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; } @@ -109,6 +181,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; } @@ -120,9 +206,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 40b0e6c1b4..ce47c670d7 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 3cb4b11cb7..e9537d8c74 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -587,6 +587,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(); @@ -1233,7 +1236,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); @@ -1246,6 +1249,7 @@ void CargoPayment::PayFinalDelivery(const CargoPacket *cp, uint count) /* For Infrastructure patch. Handling transfers between other companies */ this->route_profit += profit; + cp->PayDeferredPayments(); /* The vehicle's profit is whatever route profit there is minus feeder shares. */ this->visual_profit += profit; @@ -1257,7 +1261,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, @@ -1271,7 +1275,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 dc2a8edabe..a2aabdbcea 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -34,6 +34,7 @@ #include "bridge_signal_map.h" #include "command_func.h" #include "zoning.h" +#include "cargopacket.h" #include "safeguards.h" @@ -86,6 +87,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin LinkGraphSchedule::Clear(); ClearTraceRestrictMapping(); ClearBridgeSimulatedSignalMapping(); + ClearCargoPacketDeferredPayments(); PoolBase::Clean(PT_NORMAL); FreeSignalPrograms(); diff --git a/src/openttd.cpp b/src/openttd.cpp index b4c1accb91..b299f934c8 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -70,6 +70,7 @@ #include "thread/thread.h" #include "bridge_signal_map.h" #include "zoning.h" +#include "cargopacket.h" #include "linkgraph/linkgraphschedule.h" #include "tracerestrict.h" @@ -344,6 +345,7 @@ static void ShutdownGame() LinkGraphSchedule::Clear(); ClearTraceRestrictMapping(); ClearBridgeSimulatedSignalMapping(); + ClearCargoPacketDeferredPayments(); PoolBase::Clean(PT_ALL); FreeSignalPrograms(); 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 0efb035242..ce14306d39 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -61,7 +61,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_AUTO_TIMETABLE, XSCF_NULL, 4, 4, "auto_timetables", NULL, NULL, NULL }, { XSLFI_VEHICLE_REPAIR_COST, XSCF_NULL, 2, 2, "vehicle_repair_cost", NULL, NULL, NULL }, { XSLFI_ENH_VIEWPORT_PLANS, XSCF_IGNORABLE_ALL, 1, 1, "enh_viewport_plans", NULL, NULL, "PLAN,PLLN" }, - { 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_VARIABLE_DAY_LENGTH, XSCF_NULL, 2, 2, "variable_day_length", NULL, NULL, NULL }, { XSLFI_ORDER_OCCUPANCY, XSCF_NULL, 2, 2, "order_occupancy", NULL, NULL, NULL }, { XSLFI_MORE_COND_ORDERS, XSCF_NULL, 1, 1, "more_cond_orders", NULL, NULL, NULL },