summaryrefslogtreecommitdiff
path: root/src/economy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/economy.cpp')
-rw-r--r--src/economy.cpp201
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;