diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index d898e13a94..d317749115 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -955,6 +955,10 @@ RelativePath=".\..\src\driver.h" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 16e804b2b9..347e0dccf2 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -952,6 +952,10 @@ RelativePath=".\..\src\driver.h" > + + diff --git a/source.list b/source.list index 508cd10efb..0653255ca2 100644 --- a/source.list +++ b/source.list @@ -167,6 +167,7 @@ direction_func.h direction_type.h music/dmusic.h driver.h +economy_base.h economy_func.h economy_type.h effectvehicle_base.h diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index e8e85b643e..9907acec06 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -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()); if (amount <= 0) continue; - src->cargo.MoveTo(&dest->cargo, amount); + src->cargo.MoveTo(&dest->cargo, amount, CargoList::MTA_UNLOAD, NULL); } } diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index f94363fd9d..20e7f306f4 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -5,6 +5,7 @@ #include "stdafx.h" #include "station_base.h" #include "oldpool_func.h" +#include "economy_base.h" /* Initialize the cargopacket-pool */ DEFINE_OLD_POOL_GENERIC(CargoPacket, CargoPacket) @@ -27,7 +28,6 @@ CargoPacket::CargoPacket(StationID source, uint16 count) this->count = count; this->days_in_transit = 0; this->feeder_share = 0; - this->paid_for = false; } CargoPacket::~CargoPacket() @@ -37,7 +37,7 @@ CargoPacket::~CargoPacket() bool CargoPacket::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,11 +81,6 @@ uint CargoList::Count() const return count; } -bool CargoList::UnpaidCargo() const -{ - return unpaid_cargo; -} - Money CargoList::FeederShare() const { return feeder_share; @@ -146,9 +141,10 @@ void CargoList::Truncate(uint count) 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_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL); CargoList tmp; while (!packets.empty() && count > 0) { @@ -161,20 +157,25 @@ bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, if (cp->source == data) { tmp.Append(cp); } else { + payment->PayFinalDelivery(cp, cp->count); count -= cp->count; delete cp; } - break; + continue; // of the loop + case MTA_CARGO_LOAD: cp->loaded_at_xy = data; - /* When cargo is moved into another vehicle you have *always* paid for it */ - cp->paid_for = false; - /* FALL THROUGH */ - case MTA_OTHER: - count -= cp->count; - dest->packets.push_back(cp); + break; + + case MTA_TRANSFER: + payment->PayTransfer(cp, cp->count); + break; + + case MTA_UNLOAD: break; } + count -= cp->count; + dest->packets.push_back(cp); } else { /* Can move only part of the packet, so split it into two pieces */ if (mta != MTA_FINAL_DELIVERY) { @@ -189,11 +190,13 @@ bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, cp_new->days_in_transit = cp->days_in_transit; 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; dest->packets.push_back(cp_new); + + if (mta == MTA_TRANSFER) payment->PayTransfer(cp_new, count); + } else { + payment->PayFinalDelivery(cp, count); } cp->count -= count; @@ -205,7 +208,7 @@ bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, if (mta == MTA_FINAL_DELIVERY && !tmp.Empty()) { /* 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(); } @@ -219,7 +222,6 @@ void CargoList::InvalidateCache() { empty = packets.empty(); count = 0; - unpaid_cargo = false; feeder_share = 0; source = INVALID_STATION; days_in_transit = 0; @@ -229,7 +231,6 @@ void CargoList::InvalidateCache() uint dit = 0; for (List::const_iterator it = packets.begin(); it != packets.end(); it++) { count += (*it)->count; - unpaid_cargo |= !(*it)->paid_for; dit += (*it)->days_in_transit * (*it)->count; feeder_share += (*it)->feeder_share; } diff --git a/src/cargopacket.h b/src/cargopacket.h index 12014d3fd9..2e25442c8b 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -29,7 +29,6 @@ struct CargoPacket : PoolItem { uint16 count; ///< The amount of cargo in this packet 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 @@ -85,7 +84,8 @@ public: enum MoveToAction { 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_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: @@ -93,7 +93,6 @@ private: bool empty; ///< Cache for whether this list is empty or not 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 StationID source; ///< Cache for the source of the packet uint days_in_transit; ///< Cache for the number of days in transit @@ -129,12 +128,6 @@ public: */ uint Count() const; - /** - * Is there some cargo that has not been paid for? - * @return true if and only if there is such a cargo - */ - bool UnpaidCargo() const; - /** * Returns total sum of the feeder share for all packets * @return the before mentioned number @@ -175,18 +168,23 @@ public: * 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_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 count the amount of cargo entities to move * @param mta how to handle the moving (side effects) * @param data Depending on mta the data of this variable differs: * - MTA_FINAL_DELIVERY - station ID of packet's origin not to remove * - MTA_CARGO_LOAD - station's tile index of load - * - MTA_OTHER - unused - * @param mta == MTA_FINAL_DELIVERY || dest != NULL + * - MTA_TRANSFER - unused + * - 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 */ - 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 */ void InvalidateCache(); diff --git a/src/economy.cpp b/src/economy.cpp index 2f3d36a5fd..8c09caef40 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -32,10 +32,16 @@ #include "autoreplace_func.h" #include "company_gui.h" #include "signs_base.h" +#include "economy_base.h" +#include "oldpool_func.h" #include "table/strings.h" #include "table/sprites.h" + +/* Initialize the cargo payment-pool */ +DEFINE_OLD_POOL_GENERIC(CargoPayment, CargoPayment) + /** * Multiply two integer values and shift the results to right. * @@ -1220,15 +1226,17 @@ static bool FindIndustryToDeliver(TileIndex ind_tile, void *user_data) return true; } +/** The industries we've currently brought cargo to. */ +static SmallIndustryList _cargo_delivery_destinations; + /** * Transfer goods from station to industry. * All cargo is delivered to the nearest (Manhattan) industry to the station sign, which is inside the acceptance rectangle and actually accepts the cargo. * @param st The station that accepted the cargo * @param cargo_type Type of cargo delivered * @param nun_pieces Amount of cargo delivered - * @param industry_set The destination industry will be inserted into this set */ -static void DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, int num_pieces, SmallIndustryList *industry_set) +static void DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, int num_pieces) { if (st->rect.IsEmpty()) return; @@ -1267,13 +1275,14 @@ static void DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, int nu assert(best != NULL); /* Insert the industry into industry_set, if not yet contained */ - if (industry_set != NULL) industry_set->Include(best); + _cargo_delivery_destinations.Include(best); best->incoming_cargo_waiting[accepted_cargo_index] = min(num_pieces + best->incoming_cargo_waiting[accepted_cargo_index], 0xFFFF); } } -static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type) + +static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type, CompanyID company) { Subsidy *s; TileIndex xy; @@ -1325,7 +1334,7 @@ static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type) pair = SetupSubsidyDecodeParam(s, 0); InjectDParam(1); - SetDParam(0, _current_company); + SetDParam(0, company); AddNewsItem( STR_2031_SERVICE_SUBSIDY_AWARDED + _settings_game.difficulty.subsidy_multiplier, NS_SUBSIDIES, @@ -1347,10 +1356,10 @@ static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type) * @param dest Station the cargo has been unloaded * @param source_tile The origin of the cargo for distance calculation * @param days_in_transit Travel time - * @param industry_set The delivered industry will be inserted into this set, if not yet contained + * @param company The company delivering the cargo * The cargo is just added to the stockpile of the industry. It is due to the caller to trigger the industry's production machinery */ -static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, TileIndex source_tile, byte days_in_transit, SmallIndustryList *industry_set) +static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, TileIndex source_tile, byte days_in_transit, Company *company) { bool subsidised; Station *s_from, *s_to; @@ -1359,18 +1368,15 @@ static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, assert(num_pieces > 0); /* Update company statistics */ - { - Company *c = GetCompany(_current_company); - c->cur_economy.delivered_cargo += num_pieces; - SetBit(c->cargo_types, cargo_type); - } + company->cur_economy.delivered_cargo += num_pieces; + SetBit(company->cargo_types, cargo_type); /* Get station pointers. */ s_from = IsValidStationID(source) ? GetStation(source) : NULL; s_to = GetStation(dest); /* Check if a subsidy applies. */ - subsidised = s_from != NULL && CheckSubsidised(s_from, s_to, cargo_type); + subsidised = s_from != NULL && CheckSubsidised(s_from, s_to, cargo_type, company->index); /* Increase town's counter for some special goods types */ const CargoSpec *cs = GetCargo(cargo_type); @@ -1378,7 +1384,7 @@ static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, if (cs->town_effect == TE_WATER) s_to->town->new_act_water += num_pieces; /* Give the goods to the industry. */ - DeliverGoodsToIndustry(s_to, cargo_type, num_pieces, industry_set); + DeliverGoodsToIndustry(s_to, cargo_type, num_pieces); /* Determine profit */ profit = GetTransportedGoodsIncome(num_pieces, DistanceManhattan(source_tile, s_to->xy), days_in_transit, cargo_type); @@ -1432,109 +1438,107 @@ static void TriggerIndustryProduction(Industry *i) } /** - * Performs the vehicle payment _and_ marks the vehicle to be unloaded. + * Makes us a new cargo payment helper. + * @param front The front of the train + * @param destinations List to add the destinations of 'our' cargo to + */ +CargoPayment::CargoPayment(Vehicle *front) : + front(front), + current_station(front->last_station_visited) +{ +} + +CargoPayment::~CargoPayment() +{ + if (this->CleaningPool()) return; + + this->front->cargo_payment = NULL; + + if (this->visual_profit == 0) { + this->front = NULL; + return; + } + + CompanyID old_company = _current_company; + _current_company = this->front->owner; + + SubtractMoneyFromCompany(CommandCost(this->front->GetExpenseType(true), -this->route_profit)); + this->front->profit_this_year += this->visual_profit << 8; + + if (this->route_profit != 0) { + if (IsLocalCompany() && !PlayVehicleSound(this->front, VSE_LOAD_UNLOAD)) { + SndPlayVehicleFx(SND_14_CASHTILL, this->front); + } + + ShowCostOrIncomeAnimation(this->front->x_pos, this->front->y_pos, this->front->z_pos, -this->visual_profit); + } else { + ShowFeederIncomeAnimation(this->front->x_pos, this->front->y_pos, this->front->z_pos, this->visual_profit); + } + + _current_company = old_company; + + this->front = NULL; +} + +/** + * Handle payment for final delivery of the given cargo packet. + * @param cp The cargo packet to pay for. + * @param count The number of packets to pay for. + */ +void CargoPayment::PayFinalDelivery(CargoPacket *cp, uint count) +{ + if (this->owner == NULL) { + this->owner = GetCompany(this->front->owner); + } + + /* Handle end of route payment */ + Money profit = DeliverGoods(count, this->ct, cp->source, this->current_station, cp->source_xy, cp->days_in_transit, this->owner); + this->route_profit += profit; + + /* The vehicle's profit is whatever route profit there is minus feeder shares. */ + this->visual_profit += profit - cp->feeder_share; +} + +/** + * Handle payment for transfer of the given cargo packet. + * @param cp The cargo packet to pay for. + * @param count The number of packets to pay for. + */ +void CargoPayment::PayTransfer(CargoPacket *cp, uint count) +{ + Money profit = GetTransportedGoodsIncome( + count, + /* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */ + DistanceManhattan(cp->loaded_at_xy, GetStation(this->current_station)->xy), + cp->days_in_transit, + this->ct); + + 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 +} + +/** + * Prepare 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 = GetStation(last_visited); - - /* The owner of the train wants to be paid */ - CompanyID old_company = _current_company; - _current_company = front_v->owner; - /* At this moment loading cannot be finished */ ClrBit(front_v->vehicle_flags, VF_LOADING_FINISHED); /* Start unloading in at the first possible moment */ front_v->load_unload_time_rem = 1; - /* Collect delivered industries */ - static SmallIndustryList industry_set; - industry_set.Clear(); - - for (Vehicle *v = front_v; v != NULL; v = v->Next()) { - /* 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(); - - 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; - - /* handle end of route payment */ - Money profit = DeliverGoods(cp->count, v->cargo_type, cp->source, last_visited, cp->source_xy, cp->days_in_transit, &industry_set); - cp->paid_for = true; - route_profit += profit; // display amount paid for final route delivery, A-D of a chain A-B-C-D - vehicle_profit += profit - cp->feeder_share; // whole vehicle is not payed for transfers picked up earlier - - 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) { - Money profit = GetTransportedGoodsIncome( - cp->count, - /* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */ - DistanceManhattan(cp->loaded_at_xy, GetStation(last_visited)->xy), - cp->days_in_transit, - v->cargo_type); - - front_v->profit_this_year += profit << 8; - virtual_profit += profit; // accumulate transfer profits for whole vehicle - cp->feeder_share += profit; // account for the (virtual) profit already made for the cargo packet - cp->paid_for = true; // record that the cargo has been paid for to eliminate double counting - } - result |= 2; - + if ((front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) { + for (Vehicle *v = front_v; v != NULL; v = v->Next()) { + if (v->cargo_cap > 0 && !v->cargo.Empty()) { SetBit(v->vehicle_flags, VF_CARGO_UNLOADING); } } - v->cargo.InvalidateCache(); } - /* Call the production machinery of industries only once for every vehicle chain */ - const Industry * const *isend = industry_set.End(); - for (Industry **iid = industry_set.Begin(); iid != isend; iid++) { - TriggerIndustryProduction(*iid); - } - - if (virtual_profit > 0) { - ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit); - } - - if (route_profit != 0) { - front_v->profit_this_year += vehicle_profit << 8; - SubtractMoneyFromCompany(CommandCost(front_v->GetExpenseType(true), -route_profit)); - - if (IsLocalCompany() && !PlayVehicleSound(front_v, VSE_LOAD_UNLOAD)) { - SndPlayVehicleFx(SND_14_CASHTILL, front_v); - } - - ShowCostOrIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, -vehicle_profit); - } - - _current_company = old_company; + assert(front_v->cargo_payment == NULL); + front_v->cargo_payment = new CargoPayment(front_v); } /** @@ -1583,6 +1587,8 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) v->cur_speed = 0; + CargoPayment *payment = v->cargo_payment; + for (; v != NULL; v = v->Next()) { if (v->cargo_cap == 0) continue; @@ -1604,9 +1610,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 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)) { /* 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; accepted = true; @@ -1618,14 +1626,20 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) * station is still accepting the cargo in the vehicle. It doesn't * accept cargo that was loaded at the same station. */ 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); result |= 2; } else if (!accepted) { /* The order changed while unloading (unset unload/transfer) or the - * station does not accept goods anymore. */ + * station does not accept our goods. */ ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING); + + /* Say we loaded something, otherwise we'll think we didn't unload + * something and we didn't load something, so we must be finished + * at this station. Setting the unloaded means that we will get a + * retry for loading in the next cycle. */ + anything_unloaded = true; continue; } @@ -1695,7 +1709,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) completely_emptied = false; 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->last_vehicle_type = v->type; @@ -1731,6 +1745,8 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) v = u; + if (!anything_unloaded) delete payment; + if (anything_loaded || anything_unloaded) { if (_settings_game.order.gradual_loading) { /* The time it takes to load one 'slice' of cargo or passengers depends @@ -1807,6 +1823,9 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) */ void LoadUnloadStation(Station *st) { + /* No vehicle is here... */ + if (st->loading_vehicles.empty()) return; + int cargo_left[NUM_CARGO]; for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count(); @@ -1816,6 +1835,13 @@ void LoadUnloadStation(Station *st) Vehicle *v = *iter; 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() diff --git a/src/economy_base.h b/src/economy_base.h new file mode 100644 index 0000000000..62d79deb94 --- /dev/null +++ b/src/economy_base.h @@ -0,0 +1,51 @@ +/* $Id$ */ + +/** @file economy_base.h Base classes related to the economy. */ + +#ifndef ECONOMY_BASE_H +#define ECONOMY_BASE_H + +#include "cargopacket.h" + +DECLARE_OLD_POOL(CargoPayment, CargoPayment, 9, 125) + +/** + * Helper class to perform the cargo payment. + */ +struct CargoPayment : PoolItem { + 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 visual_profit; ///< The visual profit to show + + /* Unsaved variables */ + Company *owner; ///< The owner of the vehicle + StationID current_station; ///< The current station + CargoID ct; ///< The currently handled cargo type + + /** Constructor for pool saveload */ + CargoPayment() {} + CargoPayment(Vehicle *front); + ~CargoPayment(); + + void PayTransfer(CargoPacket *cp, uint count); + void PayFinalDelivery(CargoPacket *cp, uint count); + + /** + * Sets the currently handled cargo type. + * @param ct the cargo type to handle from now on. + */ + void SetCargo(CargoID ct) { this->ct = ct; } + + inline bool IsValid() const { return this->front != NULL; } +}; + +#define FOR_ALL_CARGO_PAYMENTS_FROM(v, start) for (v = GetCargoPayment(start); v != NULL; v = (v->index + 1U < GetCargoPaymentPoolSize()) ? GetCargoPayment(v->index + 1) : NULL) if (v->IsValid()) +#define FOR_ALL_CARGO_PAYMENTS(var) FOR_ALL_CARGO_PAYMENTS_FROM(var, 0) + +/** + * Whether this savegame is a savegame with cargo payment or not. + * This is important to know when loading a savegame. + */ +extern bool _cargo_payment_savegame; + +#endif /* ECONOMY_BASE_H */ diff --git a/src/economy_func.h b/src/economy_func.h index 8bbb4a7ccb..40b86cb7be 100644 --- a/src/economy_func.h +++ b/src/economy_func.h @@ -39,7 +39,7 @@ void StartupIndustryDailyChanges(bool init_counter); 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); -void VehiclePayment(Vehicle *front_v); +void PrepareUnload(Vehicle *front_v); void LoadUnloadStation(Station *st); Money GetPriceByIndex(uint8 index); diff --git a/src/economy_type.h b/src/economy_type.h index 0b7b581d36..59c3c63ba1 100644 --- a/src/economy_type.h +++ b/src/economy_type.h @@ -136,4 +136,7 @@ enum { LOAN_INTERVAL_OLD_AI = 50000, }; +struct CargoPayment; +typedef uint32 CargoPaymentID; + #endif /* ECONOMY_TYPE_H */ diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 553164c353..e06b3eaeff 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -30,6 +30,7 @@ #include "../ai/ai.hpp" #include "../animated_tile_func.h" +#include "../economy_base.h" #include "table/strings.h" #include "saveload_internal.h" @@ -541,6 +542,13 @@ bool AfterLoadGame() if (!IsValidCompanyID(COMPANY_FIRST) && (!_networking || (_networking && _network_server && !_network_dedicated))) 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)) { /* Locks/shiplifts in very old savegames had OWNER_WATER as owner */ for (TileIndex t = 0; t < MapSize(); t++) { @@ -1269,13 +1277,8 @@ bool AfterLoadGame() * stored to stop people cheating and cashing in several times. This * 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 - * amount of cargo that has been paid for is stored. */ + * amount that has been paid is stored. */ 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); v->cargo.InvalidateCache(); } @@ -1846,6 +1849,22 @@ bool AfterLoadGame() } } + if (!_cargo_payment_savegame) { + /* 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. + */ + Station *st; + FOR_ALL_STATIONS(st) { + std::list::iterator iter; + for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) { + Vehicle *v = *iter; + assert(v->cargo_payment == NULL); + v->cargo_payment = new CargoPayment(v); + } + } + } + if (CheckSavegameVersion(122)) { /* Animated tiles would sometimes not be actually animated or * in case of old savegames duplicate. */ @@ -1926,6 +1945,7 @@ void ReloadNewGRFData() StartupEngines(); SetCachedEngineCounts(); /* update station and waypoint graphics */ + AfterLoadWaypoints(); AfterLoadStations(); /* Check and update house and town values */ diff --git a/src/saveload/cargopacket_sl.cpp b/src/saveload/cargopacket_sl.cpp index c871d66c81..a73e19db77 100644 --- a/src/saveload/cargopacket_sl.cpp +++ b/src/saveload/cargopacket_sl.cpp @@ -8,13 +8,15 @@ #include "saveload.h" static const SaveLoad _cargopacket_desc[] = { - SLE_VAR(CargoPacket, source, SLE_UINT16), - SLE_VAR(CargoPacket, source_xy, SLE_UINT32), - SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32), - SLE_VAR(CargoPacket, count, SLE_UINT16), - SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8), - SLE_VAR(CargoPacket, feeder_share, SLE_INT64), - SLE_VAR(CargoPacket, paid_for, SLE_BOOL), + SLE_VAR(CargoPacket, source, SLE_UINT16), + SLE_VAR(CargoPacket, source_xy, SLE_UINT32), + SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32), + SLE_VAR(CargoPacket, count, SLE_UINT16), + SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8), + SLE_VAR(CargoPacket, feeder_share, SLE_INT64), + + /* Used to be paid_for, but that got changed. */ + SLE_CONDNULL(1, 0, 120), SLE_END() }; diff --git a/src/saveload/economy_sl.cpp b/src/saveload/economy_sl.cpp index 99f306f527..7d65ccc58a 100644 --- a/src/saveload/economy_sl.cpp +++ b/src/saveload/economy_sl.cpp @@ -4,9 +4,12 @@ #include "../stdafx.h" #include "../economy_func.h" +#include "../economy_base.h" #include "saveload.h" +bool _cargo_payment_savegame = false; + /** Prices */ static void SaveLoad_PRIC() { @@ -51,7 +54,37 @@ static void Load_ECMY() 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); + } + + _cargo_payment_savegame = true; +} + extern const ChunkHandler _economy_chunk_handlers[] = { + { 'CAPY', Save_CAPY, Load_CAPY, CH_ARRAY}, { 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH}, { 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH}, { 'ECMY', Save_ECMY, Load_ECMY, CH_RIFF | CH_LAST}, diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 2f9e22d9bf..6a4c1c115c 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -38,6 +38,7 @@ #include "settings_type.h" #include "network/network.h" +#include "economy_base.h" #include "table/sprites.h" #include "table/strings.h" @@ -481,6 +482,11 @@ void InitializeVehicles() _Vehicle_pool.CleanPool(); _Vehicle_pool.AddBlockToPool(); + _CargoPayment_pool.CleanPool(); + _CargoPayment_pool.AddBlockToPool(); + + _cargo_payment_savegame = false; + _vehicles_to_autoreplace.Reset(); ResetVehiclePosHash(); } @@ -1514,7 +1520,7 @@ void Vehicle::BeginLoading() GetStation(this->last_station_visited)->loading_vehicles.push_back(this); - VehiclePayment(this); + PrepareUnload(this); InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner); InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH); @@ -1530,6 +1536,8 @@ void Vehicle::LeaveStation() { assert(current_order.IsType(OT_LOADING)); + delete this->cargo_payment; + /* Only update the timetable if the vehicle was supposed to stop here. */ if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false); diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 927bfbd774..9baf817b0f 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -223,7 +223,9 @@ public: 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 value; + Money value; ///< Value of the vehicle + + CargoPayment *cargo_payment; ///< The cargo payment we're currently in /* Used for timetabling. */ uint32 current_order_time; ///< How many ticks have passed since this order started.