diff options
Diffstat (limited to 'src/economy.cpp')
-rw-r--r-- | src/economy.cpp | 201 |
1 files changed, 148 insertions, 53 deletions
diff --git a/src/economy.cpp b/src/economy.cpp index 39cb8ddaa..424f3c29f 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -46,6 +46,7 @@ #include "game/game.hpp" #include "cargomonitor.h" #include "goal_base.h" +#include "cargodest_func.h" #include "table/strings.h" #include "table/pricebase.h" @@ -493,6 +494,13 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner) /* if a company goes bankrupt, set owner to OWNER_NONE so the sign doesn't disappear immediately * also, drawing station window would cause reading invalid company's colour */ st->owner = new_owner == INVALID_OWNER ? OWNER_NONE : new_owner; + + /* Move route links to the new company. */ + for (CargoID cid = 0; cid < NUM_CARGO; cid++) { + for (RouteLinkList::iterator itr = st->goods[cid].routes.begin(); itr != st->goods[cid].routes.end(); ++itr) { + if ((*itr)->owner == old_owner) (*itr)->owner = new_owner; + } + } } } @@ -985,6 +993,33 @@ Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, C /** The industries we've currently brought cargo to. */ static SmallIndustryList _cargo_delivery_destinations; + /** + * Deliver goods to an industry. Cargo acceptance by the industry is checked. + * @param ind The industry to deliver to. + * @param cargo_type Type of cargo delivered. + * @param num_pieces Amount of cargo delivered. + * @return Accepted pieces of cargo. + */ +static uint DeliverGoodsToIndustry(Industry *ind, CargoID cargo_type, uint num_pieces) +{ + uint cargo_index; + for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) { + if (cargo_type == ind->accepts_cargo[cargo_index]) break; + } + /* Check if matching cargo has been found */ + if (cargo_index >= lengthof(ind->accepts_cargo)) return 0; + + /* Check if industry temporarily refuses acceptance */ + if (IndustryTemporarilyRefusesCargo(ind, cargo_type)) return 0; + + /* Insert the industry into _cargo_delivery_destinations, if not yet contained */ + _cargo_delivery_destinations.Include(ind); + + uint amount = min(num_pieces, 0xFFFFU - ind->incoming_cargo_waiting[cargo_index]); + ind->incoming_cargo_waiting[cargo_index] += amount; + return amount; +} + /** * 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. @@ -1009,21 +1044,7 @@ static uint DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, uint n Industry *ind = st->industries_near[i]; if (ind->index == source) continue; - uint cargo_index; - for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) { - if (cargo_type == ind->accepts_cargo[cargo_index]) break; - } - /* Check if matching cargo has been found */ - if (cargo_index >= lengthof(ind->accepts_cargo)) continue; - - /* Check if industry temporarily refuses acceptance */ - if (IndustryTemporarilyRefusesCargo(ind, cargo_type)) continue; - - /* Insert the industry into _cargo_delivery_destinations, if not yet contained */ - _cargo_delivery_destinations.Include(ind); - - uint amount = min(num_pieces, 0xFFFFU - ind->incoming_cargo_waiting[cargo_index]); - ind->incoming_cargo_waiting[cargo_index] += amount; + uint amount = DeliverGoodsToIndustry(ind, cargo_type, num_pieces); num_pieces -= amount; accepted += amount; } @@ -1041,17 +1062,25 @@ static uint DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, uint n * @param company The company delivering the cargo * @param src_type Type of source of cargo (industry, town, headquarters) * @param src Index of source of cargo + * @param cp_dest_type Type of destination of cargo + * @param cp_dest Index of the destination of cargo * @return Revenue for delivering cargo * @note 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 dest, TileIndex source_tile, byte days_in_transit, Company *company, SourceType src_type, SourceID src) +static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID dest, TileIndex source_tile, byte days_in_transit, Company *company, SourceType src_type, SourceID src, SourceType cp_dest_type, SourceID cp_dest) { assert(num_pieces > 0); Station *st = Station::Get(dest); - /* Give the goods to the industry. */ - uint accepted = DeliverGoodsToIndustry(st, cargo_type, num_pieces, src_type == ST_INDUSTRY ? src : INVALID_INDUSTRY); + uint accepted = 0; + if (cp_dest != INVALID_SOURCE) { + /* If this cargo has an industry as destination, deliver the cargo to it. */ + if (cp_dest_type == ST_INDUSTRY) accepted = DeliverGoodsToIndustry(Industry::Get(cp_dest), cargo_type, num_pieces); + } else { + /* Give the goods to any accepting industry. */ + accepted = DeliverGoodsToIndustry(st, cargo_type, num_pieces, src_type == ST_INDUSTRY ? src : INVALID_INDUSTRY); + } /* If this cargo type is always accepted, accept all */ if (HasBit(st->always_accepted, cargo_type)) accepted = num_pieces; @@ -1140,21 +1169,29 @@ CargoPayment::~CargoPayment() this->front->cargo_payment = NULL; - if (this->visual_profit == 0) return; + if (this->visual_profit == 0 && this->transfer_profit == 0) return; Backup<CompanyByte> cur_company(_current_company, this->front->owner, FILE_LINE); SubtractMoneyFromCompany(CommandCost(this->front->GetExpenseType(true), -this->route_profit)); - this->front->profit_this_year += this->visual_profit << 8; + this->front->profit_this_year += (this->visual_profit + this->transfer_profit) << 8; + + int z = this->front->z_pos; if (this->route_profit != 0) { - if (IsLocalCompany() && !PlayVehicleSound(this->front, VSE_LOAD_UNLOAD)) { - SndPlayVehicleFx(SND_14_CASHTILL, this->front); - } + /* Show profit/loss from final delivery. */ + ShowCostOrIncomeAnimation(this->front->x_pos, this->front->y_pos, z, -this->visual_profit); + z += VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM; + } - 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); + if (this->transfer_profit != 0) { + /* Show transfer credits. */ + ShowFeederIncomeAnimation(this->front->x_pos, this->front->y_pos, z, this->transfer_profit); + } + + /* Play cash sound. */ + if (IsLocalCompany() && !PlayVehicleSound(this->front, VSE_LOAD_UNLOAD)) { + SndPlayVehicleFx(SND_14_CASHTILL, this->front); } cur_company.Restore(); @@ -1172,7 +1209,7 @@ 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()); + Money profit = DeliverGoods(count, this->ct, this->current_station, cp->SourceStationXY(), cp->DaysInTransit(), this->owner, cp->SourceSubsidyType(), cp->SourceSubsidyID(), cp->DestinationType(), cp->DestinationID()); this->route_profit += profit; /* The vehicle's profit is whatever route profit there is minus feeder shares. */ @@ -1196,7 +1233,7 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count) profit = profit * _settings_game.economy.feeder_payment_share / 100; - this->visual_profit += profit; // accumulate transfer profits for whole vehicle + this->transfer_profit += profit; // accumulate transfer profits for whole vehicle return profit; // account for the (virtual) profit already made for the cargo packet } @@ -1252,10 +1289,12 @@ static bool IsArticulatedVehicleEmpty(Vehicle *v) * picked up by another vehicle when all * previous vehicles have loaded. */ -static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) +static void LoadUnloadVehicle(Vehicle *front, StationCargoList::OrderMap (&cargo_left)[NUM_CARGO]) { assert(front->current_order.IsType(OT_LOADING)); + OrderID last_order = front->last_order_id; + /* We have not waited enough time till the next round of loading/unloading */ if (front->load_unload_ticks != 0) { if (_settings_game.order.improved_load && (front->current_order.GetLoadType() & OLFB_FULL_LOAD)) { @@ -1263,7 +1302,15 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) for (Vehicle *v = front; v != NULL; v = v->Next()) { int cap_left = v->cargo_cap; if (!HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) cap_left -= v->cargo.Count(); - if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left; + if (cap_left > 0) { + /* Try the bucket for our next destination first. */ + int loaded = min(cap_left, cargo_left[v->cargo_type][last_order]); + cargo_left[v->cargo_type][last_order] -= loaded; + + /* Reserve from the common bucket if still space left. */ + loaded = min(cap_left - loaded, cargo_left[v->cargo_type][INVALID_ORDER]); + cargo_left[v->cargo_type][INVALID_ORDER] -= loaded; + } } } return; @@ -1344,15 +1391,44 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, load_amount) : cargo_count; 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 did_transfer = false; payment->SetCargo(v->cargo_type); - if (HasBit(ge->acceptance_pickup, GoodsEntry::GES_ACCEPTANCE) && !(front->current_order.GetUnloadType() & OUFB_TRANSFER)) { - /* The cargo has reached its final destination, the packets may now be destroyed */ - remaining = v->cargo.MoveTo<StationCargoList>(NULL, amount_unloaded, VehicleCargoList::MTA_FINAL_DELIVERY, payment, last_visited); + if (CargoHasDestinations(v->cargo_type)) { + /* This cargo type has destinations enabled, this means explicit transfer + * orders are overridden for cargo packets with destination. + * Default action for destination-less cargo packets is final delivery when + * accepted, otherwise no action. If the current order is forced unload, + * always unload all cargo. */ + VehicleCargoList::MoveToAction mta = HasBit(ge->acceptance_pickup, GoodsEntry::GES_ACCEPTANCE) ? VehicleCargoList::MTA_FINAL_DELIVERY : VehicleCargoList::MTA_NO_ACTION; + if (front->current_order.GetUnloadType() & OUFB_UNLOAD) mta = VehicleCargoList::MTA_UNLOAD; //front was u + remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded, mta, payment, last_visited, last_order, v->cargo_type, &did_transfer); dirty_vehicle = true; accepted = true; + } else { + /* Cargo destinations are not enabled, handle transfer orders. */ + if (HasBit(ge->acceptance_pickup, GoodsEntry::GES_ACCEPTANCE) && !(front->current_order.GetUnloadType() & OUFB_TRANSFER)) { //front was u + /* The cargo has reached its final destination, the packets may now be destroyed */ + remaining = v->cargo.MoveTo<StationCargoList>(NULL, amount_unloaded, VehicleCargoList::MTA_FINAL_DELIVERY, payment, last_visited, v->cargo_type); + + dirty_vehicle = true; + accepted = true; + } + + /* The !accepted || v->cargo.Count == cargo_count clause is there + * to make it possible to force unload vehicles at the station where + * they were loaded, but to not force unload the vehicle when the + * station is still accepting the cargo in the vehicle. It doesn't + * accept cargo that was loaded at the same station. */ + if ((front->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) && (!accepted || v->cargo.Count() == cargo_count)) { //front was u + remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded, front->current_order.GetUnloadType() & OUFB_TRANSFER ? VehicleCargoList::MTA_TRANSFER : VehicleCargoList::MTA_UNLOAD, payment, last_visited, v->cargo_type); //front was u + + did_transfer = true; + dirty_vehicle = true; + accepted = true; + } } /* The !accepted || v->cargo.Count == cargo_count clause is there @@ -1360,15 +1436,15 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) * they were loaded, but to not force unload the vehicle when the * station is still accepting the cargo in the vehicle. It doesn't * accept cargo that was loaded at the same station. */ - if ((front->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) && (!accepted || v->cargo.Count() == cargo_count)) { - remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded, front->current_order.GetUnloadType() & OUFB_TRANSFER ? VehicleCargoList::MTA_TRANSFER : VehicleCargoList::MTA_UNLOAD, payment); + if (did_transfer) { + /* Update station information. */ if (!HasBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP)) { SetBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP); InvalidateWindowData(WC_STATION_LIST, last_visited); } - - dirty_vehicle = dirty_station = true; - } else if (!accepted) { + dirty_station = true; + } + if (!accepted) { /* The order changed while unloading (unset unload/transfer) or the * station does not accept our goods. */ ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING); @@ -1432,13 +1508,13 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) FOR_EACH_SET_CARGO_ID(cid, refit_mask) { /* Consider refitting to this cargo, if other vehicles of the consist cannot * already take the cargo without refitting */ - if (cargo_left[cid] > (int)consist_capleft[cid] + amount) { + if (cargo_left[cid][last_order] + cargo_left[cid][INVALID_ORDER] > (int)consist_capleft[cid] + amount) { /* Try to find out if auto-refitting would succeed. In case the refit is allowed, * the returned refit capacity will be greater than zero. */ new_subtype = GetBestFittingSubType(v, v, cid); DoCommand(v_start->tile, v_start->index, cid | 1U << 6 | new_subtype << 8 | 1U << 16, DC_QUERY_COST, GetCmdRefitVeh(v_start)); // Auto-refit and only this vehicle including artic parts. if (_returned_refit_capacity > 0) { - amount = cargo_left[cid] - consist_capleft[cid]; + amount = cargo_left[cid][last_order] + cargo_left[cid][INVALID_ORDER] - consist_capleft[cid]; new_cid = cid; } } @@ -1491,11 +1567,12 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) int cap_left = v->cargo_cap - v->cargo.Count(); if (!ge->cargo.Empty() && cap_left > 0) { uint cap = cap_left; - uint count = ge->cargo.Count(); + uint count = ge->cargo.CountForNextHop(last_order) + ge->cargo.CountForNextHop(INVALID_ORDER); /* Skip loading this vehicle if another train/vehicle is already handling - * the same cargo type at this station */ - if (_settings_game.order.improved_load && cargo_left[v->cargo_type] <= 0) { + * the same cargo type at this station. Check the buckets for our next + * destination and the general bucket. */ + if (_settings_game.order.improved_load && cargo_left[v->cargo_type][last_order] <= 0 && cargo_left[v->cargo_type][INVALID_ORDER] <= 0) { SetBit(cargo_not_full, v->cargo_type); continue; } @@ -1507,14 +1584,23 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) } if (_settings_game.order.improved_load) { /* Don't load stuff that is already 'reserved' for other vehicles */ - cap = min((uint)cargo_left[v->cargo_type], cap); - count = cargo_left[v->cargo_type]; + count = cargo_left[v->cargo_type][last_order] + cargo_left[v->cargo_type][INVALID_ORDER]; + + /* Try the bucket for our next destination first. */ + int load_next = min<uint>(cap, cargo_left[v->cargo_type][last_order]); + + /* Reserve from the common bucket if still space left. */ + int load_common = min<uint>(cap - load_next, cargo_left[v->cargo_type][INVALID_ORDER]); + + cap = load_next + load_common; + cargo_left[v->cargo_type][last_order] -= load_next; + if (use_autorefit) { /* When using autorefit, reserve all cargo for this wagon to prevent other wagons * from feeling the need to refit. */ - uint total_cap_left = v->cargo_cap - v->cargo.Count(); - cargo_left[v->cargo_type] -= total_cap_left; + uint total_cap_left = v->cargo_cap - v->cargo.Count() - load_common; consist_capleft[v->cargo_type] -= total_cap_left; + cargo_left[v->cargo_type][INVALID_ORDER] -= total_cap_left; if (total_cap_left > cap && count > cap) { /* Remember if there are reservations left so that we don't stop * loading before they're loaded. */ @@ -1523,7 +1609,7 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) } else { /* Update cargo left; but don't reserve everything yet, so other wagons * of the same consist load in parallel. */ - cargo_left[v->cargo_type] -= cap; + cargo_left[v->cargo_type][INVALID_ORDER] -= load_common; } } @@ -1546,7 +1632,7 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) completely_emptied = false; anything_loaded = true; - ge->cargo.MoveTo(&v->cargo, cap, StationCargoList::MTA_CARGO_LOAD, NULL, st->xy); + ge->cargo.MoveTo(&v->cargo, cap, StationCargoList::MTA_CARGO_LOAD, NULL, last_visited, last_order, v->cargo_type); st->time_since_load = 0; st->last_vehicle_type = v->type; @@ -1596,7 +1682,15 @@ static void LoadUnloadVehicle(Vehicle *front, int *cargo_left) cap_left -= v->cargo.Count(); } } - if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left; + if (cap_left > 0) { + /* Try the bucket for our next destination first. */ + int loaded = min(cap_left, cargo_left[v->cargo_type][last_order]); + cargo_left[v->cargo_type][last_order] -= loaded; + + /* Reserve from the common bucket if still space left. */ + loaded = min(cap_left - loaded, cargo_left[v->cargo_type][INVALID_ORDER]); + cargo_left[v->cargo_type][INVALID_ORDER] -= loaded; + } } } @@ -1714,9 +1808,10 @@ void LoadUnloadStation(Station *st) */ if (last_loading == NULL) return; - int cargo_left[NUM_CARGO]; - - for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count(); + StationCargoList::OrderMap cargo_left[NUM_CARGO]; + for (CargoID i = 0; i < NUM_CARGO; i++) { + cargo_left[i] = st->goods[i].cargo.CountForNextHop(); + } for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) { Vehicle *v = *iter; |