src/economy.cpp
branchgamebalance
changeset 9911 0b8b245a2391
parent 9910 0b2aebc8283e
child 9912 1ac8aac92385
--- a/src/economy.cpp	Wed Jun 13 11:17:30 2007 +0000
+++ b/src/economy.cpp	Wed Jun 13 11:45:14 2007 +0000
@@ -38,6 +38,7 @@
 #include "date.h"
 #include "cargotype.h"
 #include "player_face.h"
+#include "group.h"
 
 /* Score info */
 const ScoreInfo _score_info[] = {
@@ -359,12 +360,14 @@
 					DeleteVehicle(v);
 				} else {
 					v->owner = new_player;
+					v->group_id = DEFAULT_GROUP;
 					if (IsEngineCountable(v)) GetPlayer(new_player)->num_engines[v->engine_type]++;
 					switch (v->type) {
 						case VEH_TRAIN:    if (IsFrontEngine(v)) v->unitnumber = ++num_train; break;
 						case VEH_ROAD:     v->unitnumber = ++num_road; break;
 						case VEH_SHIP:     v->unitnumber = ++num_ship; break;
 						case VEH_AIRCRAFT: if (IsNormalAircraft(v)) v->unitnumber = ++num_aircraft; break;
+						default: NOT_REACHED();
 					}
 				}
 			}
@@ -804,6 +807,7 @@
 static void FindSubsidyCargoRoute(FoundRoute *fr)
 {
 	Industry *i;
+	const IndustrySpec *ind;
 	int trans, total;
 	CargoID cargo;
 
@@ -811,14 +815,15 @@
 
 	fr->from = i = GetRandomIndustry();
 	if (i == NULL) return;
+	ind = GetIndustrySpec(i->type);
 
 	/* Randomize cargo type */
-	if (Random()&1 && i->produced_cargo[1] != CT_INVALID) {
-		cargo = i->produced_cargo[1];
+	if (HASBIT(Random(), 0) && ind->produced_cargo[1] != CT_INVALID) {
+		cargo = ind->produced_cargo[1];
 		trans = i->pct_transported[1];
 		total = i->total_production[1];
 	} else {
-		cargo = i->produced_cargo[0];
+		cargo = ind->produced_cargo[0];
 		trans = i->pct_transported[0];
 		total = i->total_production[0];
 	}
@@ -845,13 +850,19 @@
 	} else {
 		/* The destination is an industry */
 		Industry *i2 = GetRandomIndustry();
+		if (i2 == NULL) {
+			return;
+		}
+
+		ind = GetIndustrySpec(i2->type);
 
 		/* The industry must accept the cargo */
-		if (i == i2 || i == NULL ||
-				(cargo != i2->accepts_cargo[0] &&
-				cargo != i2->accepts_cargo[1] &&
-				cargo != i2->accepts_cargo[2]))
+		if (i == i2 ||
+				(cargo != ind->accepts_cargo[0] &&
+				cargo != ind->accepts_cargo[1] &&
+				cargo != ind->accepts_cargo[2])) {
 			return;
+		}
 		fr->distance = DistanceManhattan(i->xy, i2->xy);
 		fr->to = i2;
 	}
@@ -1025,34 +1036,48 @@
 
 static void DeliverGoodsToIndustry(TileIndex xy, CargoID cargo_type, int num_pieces)
 {
-	Industry* best = NULL;
-	Industry* ind;
-	uint u;
+	Industry *best = NULL;
+	Industry *ind;
+	const IndustrySpec *indspec;
+	uint best_dist;
+	uint accepted_cargo_index = 0;  ///< unlikely value, just for warning removing
 
 	/* Check if there's an industry close to the station that accepts the cargo
 	 * XXX - Think of something better to
 	 *       1) Only deliver to industries which are withing the catchment radius
 	 *       2) Distribute between industries if more then one is present */
-	u = (_patches.station_spread + 8) * 2;
+	best_dist = (_patches.station_spread + 8) * 2;
 	FOR_ALL_INDUSTRIES(ind) {
-		uint t;
+		indspec = GetIndustrySpec(ind->type);
+		uint i;
 
-		if (( cargo_type == ind->accepts_cargo[0] ||
-					cargo_type == ind->accepts_cargo[1] ||
-					cargo_type == ind->accepts_cargo[2]
-				) &&
-				ind->produced_cargo[0] != CT_INVALID &&
-				ind->produced_cargo[0] != cargo_type &&
-				(t = DistanceManhattan(ind->xy, xy)) < u) {
-			u = t;
+		if (indspec->produced_cargo[0] == CT_INVALID) continue;
+
+		for (i = 0; i < lengthof(indspec->accepts_cargo); i++) {
+			if (cargo_type == indspec->accepts_cargo[i] &&
+					(indspec->input_cargo_multiplier[i][0] != 0 || indspec->input_cargo_multiplier[i][1] != 0)) {
+				break;
+			}
+		}
+
+		/* Check if matching cargo has been found */
+		if (i == lengthof(indspec->accepts_cargo)) continue;
+
+		uint dist = DistanceManhattan(ind->xy, xy);
+
+		if (dist < best_dist) {
 			best = ind;
+			best_dist = dist;
+			accepted_cargo_index = i;
 		}
 	}
 
 	/* Found one? */
 	if (best != NULL) {
+		indspec = GetIndustrySpec(best->type);
 		best->was_cargo_delivered = true;
-		best->cargo_waiting[0] = min(best->cargo_waiting[0] + num_pieces, 0xFFFF);
+		best->cargo_waiting[0] = min(best->cargo_waiting[0] + (num_pieces * indspec->input_cargo_multiplier[accepted_cargo_index][0] / 256), 0xFFFF);
+		best->cargo_waiting[1] = min(best->cargo_waiting[1] + (num_pieces * indspec->input_cargo_multiplier[accepted_cargo_index][1] / 256), 0xFFFF);
 	}
 }
 
@@ -1171,213 +1196,222 @@
 	return profit;
 }
 
-/*
- * Returns true if Vehicle v should wait loading because other vehicle is
- * already loading the same cargo type
- * v = vehicle to load, u = GetFirstInChain(v)
+/**
+ * Performs the vehicle payment _and_ marks the vehicle to be unloaded.
+ * @param front_v the vehicle to be unloaded
  */
-static bool LoadWait(const Vehicle* v, const Vehicle* u)
+void VehiclePayment(Vehicle *front_v)
 {
-	const Vehicle *w;
-	bool has_any_cargo = false;
-
-	if (!(u->current_order.flags & OF_FULL_LOAD)) return false;
+	int result = 0;
 
-	for (w = u; w != NULL; w = w->next) {
-		if (w->cargo_count != 0) {
-			if (v->cargo_type == w->cargo_type &&
-					u->last_station_visited == w->cargo_source) {
-				return false;
+	int profit = 0;
+	int total_veh_profit = 0;         // accumulates the profit across the vehicle chain (used by trains)
+	int32 route_profit = 0;           // the grand total amount for the route. A-D of transfer chain A-B-C-D
+	int virtual_profit = 0;           // virtual profit of one vehicle element for feeder systems
+	int virtual_profit_total = 0;     // virtual profit for entire vehicle chain
+	int total_cargo_feeder_share = 0; // the feeder cash amount for the goods being loaded/unloaded in this load step
+
+	int all_vehicles_cargo_feeder_share = front_v->cargo_feeder_share; // used to hold transfer value of complete vehicle chain - used by trains
+
+	StationID last_visited = front_v->last_station_visited;
+	Station *st = GetStation(last_visited);
+
+	/* The owner of the train wants to be paid */
+	PlayerID old_player = _current_player;
+	_current_player = 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;
+
+	for (Vehicle *v = front_v; v != NULL; v = v->next) {
+		/* No cargo to unload */
+		if (v->cargo_cap == 0 || v->cargo_count == 0) continue;
+
+		SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
+		/* All cargo has already been paid for, no need to pay again */
+		if (v->cargo_count == v->cargo_paid_for) continue;
+
+		GoodsEntry *ge = &st->goods[v->cargo_type];
+
+		if (v->cargo_source != last_visited &&
+				HASBIT(ge->waiting_acceptance, 15) &&
+				(front_v->current_order.flags & OF_TRANSFER) == 0) {
+			/* Deliver goods to the station */
+			st->time_since_unload = 0;
+
+			/* handle end of route payment */
+			profit += DeliverGoods(v->cargo_count - v->cargo_paid_for, v->cargo_type, v->cargo_source, last_visited, v->cargo_source_xy, v->cargo_days);
+			v->cargo_paid_for        = v->cargo_count;
+			route_profit             = profit;                                   // display amount paid for final route delivery, A-D of a chain A-B-C-D
+			total_veh_profit         = profit - all_vehicles_cargo_feeder_share; // whole vehicle is not payed for transfers picked up earlier
+			total_cargo_feeder_share = -all_vehicles_cargo_feeder_share;         // total of transfer fees in vehicle chain needs to be zero at end of unload
+
+			v->cargo_feeder_share = 0;   // clear transfer cost per vehicle
+			result |= 1;
+		} else if (front_v->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) {
+			if ((front_v->current_order.flags & OF_TRANSFER) != 0) {
+				virtual_profit = GetTransportedGoodsIncome(
+					v->cargo_count - v->cargo_paid_for,
+					/* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */
+					DistanceManhattan(v->cargo_loaded_at_xy, GetStation(last_visited)->xy),
+					v->cargo_days,
+					v->cargo_type);
+
+				front_v->profit_this_year += virtual_profit;
+				ge->feeder_profit         += v->cargo_feeder_share + virtual_profit; // transfer cargo transfer fees to station
+				total_cargo_feeder_share  -= v->cargo_feeder_share; // accumulate deduction of feeder shares
+				v->cargo_feeder_share      = 0;                     // clear transfer cost
+
+				/* keep total of cargo unloaded (pending) for accurate cargoshare calculation on load */
+				SB(ge->unload_pending, 0, 12, GB(ge->unload_pending, 0, 12) + v->cargo_count);
+
+				virtual_profit_total += virtual_profit;   // accumulate transfer profits for whole vehicle
+				v->cargo_paid_for = v->cargo_count;       // record how much of the cargo has been paid for to eliminate double counting
 			}
-			has_any_cargo = true;
+			result |= 2;
 		}
 	}
 
-	const Station *st = GetStation(u->last_station_visited);
-	std::list<Vehicle *>::const_iterator iter;
-	for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
-		const Vehicle *x = *iter;
-		if (!(x->vehstatus & (VS_STOPPED | VS_CRASHED)) && u != x) {
-			bool other_has_any_cargo = false;
-			bool has_space_for_same_type = false;
-			bool other_has_same_type = false;
-
-			for (w = x; w != NULL; w = w->next) {
-				if (w->cargo_count < w->cargo_cap && v->cargo_type == w->cargo_type) {
-					has_space_for_same_type = true;
-				}
+	/* Ensure a negative total is only applied to the vehicle if there is value to reduce. */
+	front_v->cargo_feeder_share = max(front_v->cargo_feeder_share + total_cargo_feeder_share, 0);
 
-				if (w->cargo_count != 0) {
-					if (v->cargo_type == w->cargo_type &&
-							u->last_station_visited == w->cargo_source) {
-						other_has_same_type = true;
-					}
-					other_has_any_cargo = true;
-				}
-			}
-
-			if (has_space_for_same_type) {
-				if (other_has_same_type) return true;
-				if (other_has_any_cargo && !has_any_cargo) return true;
-			}
-		}
+	if (virtual_profit_total > 0) {
+		ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit_total);
 	}
 
-	return false;
+	if (route_profit != 0) {
+		front_v->profit_this_year += total_veh_profit;
+		SubtractMoneyFromPlayer(-route_profit);
+
+		if (IsLocalPlayer() && !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, -total_veh_profit);
+	}
+
+	_current_player = old_player;
 }
 
-int LoadUnloadVehicle(Vehicle *v, bool just_arrived)
+/**
+ * Loads/unload the vehicle if possible.
+ * @param v the vehicle to be (un)loaded
+ * @param cargo_left the amount of each cargo type that is
+ *                   virtually left on the platform to be
+ *                   picked up by another vehicle when all
+ *                   previous vehicles have loaded.
+ */
+static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 {
-	int profit = 0;
-	int total_veh_profit = 0;      // accumulates the profit across the vehicle chain (used by trains)
-	int32 route_profit = 0;        // the grand total amount for the route. A-D of transfer chain A-B-C-D
-	int virtual_profit = 0;        // virtual profit of one vehicle element for feeder systems
-	int virtual_profit_total = 0;  // virtual profit for entire vehicle chain
-	int total_cargo_feeder_share = 0;  // the feeder cash amount for the goods being loaded/unloaded in this load step
+	assert(v->current_order.type == OT_LOADING);
 
-	int unloading_time = 20;
+	/* We have not waited enough time till the next round of loading/unloading */
+	if (--v->load_unload_time_rem != 0) {
+		if (_patches.improved_load && HASBIT(v->current_order.flags, OFB_FULL_LOAD)) {
+			/* 'Reserve' this cargo for this vehicle, because we were first. */
+			for (; v != NULL; v = v->next) {
+				if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo_count;
+			}
+		}
+		return;
+	}
+
+	int unloading_time = 0;
 	Vehicle *u = v;
 	int result = 0;
-	int t;
-	uint count, cap;
-	PlayerID old_player;
-	bool completely_empty = true;
-	byte load_amount;
-	bool anything_loaded = false;
+	int cap;
 
-	assert(v->current_order.type == OT_LOADING);
+	bool completely_empty  = true;
+	bool anything_unloaded = false;
+	bool anything_loaded   = false;
+	uint32 cargo_not_full  = 0;
+	uint32 cargo_full      = 0;
+	int total_cargo_feeder_share = 0; // the feeder cash amount for the goods being loaded/unloaded in this load step
 
 	v->cur_speed = 0;
 
-	/* Loading can only have finished when all the cargo has been unloaded, and
-	 * there is nothing left to load. It's easier to clear this if the
-	 * conditions haven't been met than attempting to check them all before
-	 * enabling though. */
-	SETBIT(v->vehicle_flags, VF_LOADING_FINISHED);
-
-	old_player = _current_player;
-	_current_player = v->owner;
-
 	StationID last_visited = v->last_station_visited;
 	Station *st = GetStation(last_visited);
 
-	int all_vehicles_cargo_feeder_share = v->cargo_feeder_share; // used to hold transfer value of complete vehicle chain - used by trains
+	for (; v != NULL; v = v->next) {
+		if (v->cargo_cap == 0) continue;
 
-	for (; v != NULL; v = v->next) {
-		GoodsEntry* ge;
-		load_amount = EngInfo(v->engine_type)->load_amount;
+		byte load_amount = EngInfo(v->engine_type)->load_amount;
 		if (_patches.gradual_loading && HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_LOAD_AMOUNT)) {
 			uint16 cb_load_amount = GetVehicleCallback(CBID_VEHICLE_LOAD_AMOUNT, 0, 0, v->engine_type, v);
 			if (cb_load_amount != CALLBACK_FAILED) load_amount = cb_load_amount & 0xFF;
 		}
 
-		if (v->cargo_cap == 0) continue;
-
-		/* If the vehicle has just arrived, set it to unload. */
-		if (just_arrived) SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
+		GoodsEntry *ge = &st->goods[v->cargo_type];
+		int count = GB(ge->waiting_acceptance, 0, 12);
 
-		ge = &st->goods[v->cargo_type];
-		count = GB(ge->waiting_acceptance, 0, 12);
-
-		/* unload? */
-		if (v->cargo_count != 0 && HASBIT(v->vehicle_flags, VF_CARGO_UNLOADING)) {
+		if (HASBIT(v->vehicle_flags, VF_CARGO_UNLOADING)) {
 			uint16 amount_unloaded = _patches.gradual_loading ? min(v->cargo_count, load_amount) : v->cargo_count;
 
-			CLRBIT(u->vehicle_flags, VF_LOADING_FINISHED);
-
 			if (v->cargo_source != last_visited && ge->waiting_acceptance & 0x8000 && !(u->current_order.flags & OF_TRANSFER)) {
-				/* deliver goods to the station */
-				st->time_since_unload = 0;
-
-				unloading_time += v->cargo_count; // TTDBUG: bug in original TTD
-
-				/* handle end of route payment */
-				if (just_arrived && v->cargo_paid_for < v->cargo_count) {
-					profit += DeliverGoods(v->cargo_count - v->cargo_paid_for, v->cargo_type, v->cargo_source, last_visited, v->cargo_source_xy, v->cargo_days);
-					v->cargo_paid_for = v->cargo_count;
-					route_profit = profit;       // display amount paid for final route delivery, A-D of a chain A-B-C-D
-					total_veh_profit = profit - all_vehicles_cargo_feeder_share;  // whole vehicle is not payed for transfers picked up earlier
-					total_cargo_feeder_share = -all_vehicles_cargo_feeder_share;  // total of transfer fees in vehicle chain needs to be zero at end of unload
-					v->cargo_feeder_share = 0;   // clear transfer cost per vehicle
-				}
 				result |= 1;
-				v->cargo_count -= amount_unloaded;
-				v->cargo_paid_for -= min(amount_unloaded, v->cargo_paid_for);
-				if (_patches.gradual_loading) continue;
-
 			} else if (u->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) {
-
-				/* unload goods and let it wait at the station */
-				st->time_since_unload = 0;
-
-				/* handle transfer */
-				if (just_arrived && (u->current_order.flags & OF_TRANSFER) && v->cargo_paid_for < v->cargo_count) {
-					virtual_profit = GetTransportedGoodsIncome(
-						v->cargo_count - v->cargo_paid_for,
-						/* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */
-						DistanceManhattan(v->cargo_loaded_at_xy, GetStation(last_visited)->xy),
-						v->cargo_days,
-						v->cargo_type);
-
-					ge->feeder_profit += v->cargo_feeder_share;         // transfer cargo transfer fees to station
-					total_cargo_feeder_share -= v->cargo_feeder_share;  // accumulate deduction of feeder shares
-					v->cargo_feeder_share = 0;                          // clear transfer cost
-
-					/* keep total of cargo unloaded (pending) for accurate cargoshare calculation on load */
-					SB(ge->unload_pending, 0, 12, GB(ge->unload_pending, 0, 12) + v->cargo_count);
-
-					virtual_profit_total += virtual_profit;   // accumulate transfer profits for whole vehicle
-					v->cargo_paid_for = v->cargo_count;       // record how much of the cargo has been paid for to eliminate double counting
-				}
-
-				unloading_time += v->cargo_count;
-				t = GB(ge->waiting_acceptance, 0, 12);
-				if (t == 0) {
+				if (count == 0) {
 					/* No goods waiting at station */
-					ge->enroute_time = v->cargo_days;
-					ge->enroute_from = v->cargo_source;
+					ge->enroute_time    = v->cargo_days;
+					ge->enroute_from    = v->cargo_source;
 					ge->enroute_from_xy = v->cargo_source_xy;
 				} else {
 					/* Goods already waiting at station. Set counters to the worst value. */
 					if (v->cargo_days >= ge->enroute_time) ge->enroute_time = v->cargo_days;
 
 					if (last_visited != ge->enroute_from) {
-						ge->enroute_from = v->cargo_source;
+						ge->enroute_from    = v->cargo_source;
 						ge->enroute_from_xy = v->cargo_source_xy;
 					}
 				}
-				/* Update amount of waiting cargo */
-				SB(ge->waiting_acceptance, 0, 12, min(amount_unloaded + t, 0xFFF));
+				/* Update amount of waiting cargo. There is, however, no sense in
+				 * updating the count variable because this vehicle will not be
+				 * able to take the cargo. */
+				SB(ge->waiting_acceptance, 0, 12, min(amount_unloaded + count, 0xFFF));
 
 				/* if there is not enough to unload from pending, ensure it does not go -ve
 				 * else deduct amount actually unloaded from unload_pending */
 				SB(ge->unload_pending, 0, 12, max(GB(ge->unload_pending, 0, 12) - amount_unloaded, 0U));
 
-				if (u->current_order.flags & OF_TRANSFER) {
-					ge->feeder_profit += virtual_profit;
-					u->profit_this_year += virtual_profit;
-				}
 				result |= 2;
-				v->cargo_count -= amount_unloaded;
-				v->cargo_paid_for -= min(amount_unloaded, v->cargo_paid_for);
-				if (_patches.gradual_loading) continue;
+			} else {
+				/* The order changed while unloading (unset unload/transfer) or the
+				 * station does not accept goods anymore. */
+				CLRBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
+				continue;
 			}
 
-			if (v->cargo_count != 0) completely_empty = false;
-		}
+			/* Deliver goods to the station */
+			st->time_since_unload = 0;
 
-		/* The vehicle must have been unloaded because it is either empty, or
-		 * the UNLOADING bit is already clear in v->vehicle_flags. */
-		CLRBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
+			unloading_time += amount_unloaded;
+
+			v->cargo_count -= amount_unloaded;
+			v->cargo_paid_for -= min(amount_unloaded, v->cargo_paid_for);
+
+			anything_unloaded = true;
+			if (_patches.gradual_loading && v->cargo_count != 0) {
+				completely_empty = false;
+			} else {
+				/* We have finished unloading (cargo count == 0) */
+				CLRBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
+			}
+
+			continue;
+		}
 
 		/* We cannot have paid for more cargo than there is on board. */
 		assert(v->cargo_paid_for <= v->cargo_count);
 
-		/* don't pick up goods that we unloaded */
+		/* Do not pick up goods that we unloaded */
 		if (u->current_order.flags & OF_UNLOAD) continue;
 
 		/* update stats */
-		ge->days_since_pickup = 0;
+		int t;
 		switch (u->type) {
 			case VEH_TRAIN: t = u->u.rail.cached_max_speed; break;
 			case VEH_ROAD:  t = u->max_speed / 2;           break;
@@ -1385,6 +1419,7 @@
 		}
 
 		/* if last speed is 0, we treat that as if no vehicle has ever visited the station. */
+		ge->days_since_pickup = 0;
 		ge->last_speed = min(t, 255);
 		ge->last_age = _cur_year - v->build_year;
 
@@ -1393,11 +1428,22 @@
 		if (count != 0 &&
 				(cap = v->cargo_cap - v->cargo_count) != 0) {
 
-			if (v->cargo_count == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
-
 			/* Skip loading this vehicle if another train/vehicle is already handling
 			 * the same cargo type at this station */
-			if (_patches.improved_load && (u->current_order.flags & OF_FULL_LOAD) && LoadWait(v,u)) continue;
+			if (_patches.improved_load && cargo_left[v->cargo_type] < 0) {
+				SETBIT(cargo_not_full, v->cargo_type);
+				continue;
+			}
+
+			if (cap > count) cap = count;
+			if (_patches.gradual_loading) cap = min(cap, load_amount);
+			if (_patches.improved_load) {
+				/* Don't load stuff that is already 'reserved' for other vehicles */
+				cap = min(cargo_left[v->cargo_type], cap);
+				cargo_left[v->cargo_type] -= cap;
+			}
+
+			if (v->cargo_count == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
 
 			/* TODO: Regarding this, when we do gradual loading, we
 			 * should first unload all vehicles and then start
@@ -1409,10 +1455,6 @@
 			completely_empty = false;
 			anything_loaded = true;
 
-			if (cap > count) cap = count;
-			if (_patches.gradual_loading) cap = min(cap, load_amount);
-			if (cap < count) CLRBIT(u->vehicle_flags, VF_LOADING_FINISHED);
-
 			/* cargoshare is proportioned by the amount due to unload
 			 * Otherwise, with gradual loading, 100% of credits would be taken immediately,
 			 * even if the cargo volume represents a tiny percent of the whole.
@@ -1437,31 +1479,54 @@
 			result |= 2;
 			st->last_vehicle_type = v->type;
 		}
+
+		if (v->cargo_count == v->cargo_cap) {
+			SETBIT(cargo_full, v->cargo_type);
+		} else {
+			SETBIT(cargo_not_full, v->cargo_type);
+		}
+	}
+
+	/* We update these variables here, so gradual loading still fills
+	 * all wagons at the same time instead of using the same 'improved'
+	 * loading algorithm for the wagons (only fill wagon when there is
+	 * enough to fill the previous wagons) */
+	if (_patches.improved_load && HASBIT(u->current_order.flags, OFB_FULL_LOAD)) {
+		/* Update left cargo */
+		for (v = u; v != NULL; v = v->next) {
+			if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo_count;
+		}
 	}
 
 	v = u;
 
-	/* Ensure a negative total is only applied to the vehicle if there is value to reduce. */
-	if (!((v->cargo_feeder_share == 0) && (total_cargo_feeder_share < 0)))
-		v->cargo_feeder_share += total_cargo_feeder_share;
+	v->cargo_feeder_share += total_cargo_feeder_share;
 
-	if (_patches.gradual_loading) {
-		/* The time it takes to load one 'slice' of cargo or passengers depends
-		 * on the vehicle type - the values here are those found in TTDPatch */
-		uint gradual_loading_wait_time[] = { 40, 20, 10, 20 };
+	if (anything_loaded || anything_unloaded) {
+		if (_patches.gradual_loading) {
+			/* The time it takes to load one 'slice' of cargo or passengers depends
+			* on the vehicle type - the values here are those found in TTDPatch */
+			const uint gradual_loading_wait_time[] = { 40, 20, 10, 20 };
 
-		unloading_time = gradual_loading_wait_time[v->type];
-		if (HASBIT(v->vehicle_flags, VF_LOADING_FINISHED)) {
-			if (anything_loaded) {
-				unloading_time += 20;
-			} else {
-				unloading_time = 20;
+			unloading_time = gradual_loading_wait_time[v->type];
+		}
+	} else {
+		bool finished_loading = true;
+		if (HASBIT(v->current_order.flags, OFB_FULL_LOAD)) {
+			if (_patches.full_load_any) {
+				/* if the aircraft carries passengers and is NOT full, then
+				 * continue loading, no matter how much mail is in */
+				if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap != v->cargo_count) ||
+						(cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) { // There are stull non-full cargos
+					finished_loading = false;
+				}
+			} else if (cargo_not_full != 0) {
+				finished_loading = false;
 			}
 		}
-	}
+		unloading_time = 20;
 
-	if (virtual_profit_total > 0) {
-		ShowFeederIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, virtual_profit_total);
+		SB(v->vehicle_flags, VF_LOADING_FINISHED, 1, finished_loading);
 	}
 
 	if (v->type == VEH_TRAIN) {
@@ -1480,25 +1545,32 @@
 	}
 
 	if (result != 0) {
+		InvalidateWindow(v->GetVehicleListWindowClass(), v->owner);
 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
+
 		st->MarkTilesDirty();
+		v->MarkDirty();
 
 		if (result & 2) InvalidateWindow(WC_STATION_VIEW, last_visited);
-
-		if (route_profit != 0) {
-			v->profit_this_year += total_veh_profit;
-			SubtractMoneyFromPlayer(-route_profit);
+	}
+}
 
-			if (IsLocalPlayer() && !PlayVehicleSound(v, VSE_LOAD_UNLOAD)) {
-				SndPlayVehicleFx(SND_14_CASHTILL, v);
-			}
+/**
+ * Load/unload the vehicles in this station according to the order
+ * they entered.
+ * @param st the station to do the loading/unloading for
+ */
+void LoadUnloadStation(Station *st)
+{
+	int cargo_left[NUM_CARGO];
 
-			ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, -total_veh_profit);
-		}
+	for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = GB(st->goods[i].waiting_acceptance, 0, 12);
+
+	std::list<Vehicle *>::iterator iter;
+	for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
+		Vehicle *v = *iter;
+		if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left);
 	}
-
-	_current_player = old_player;
-	return result;
 }
 
 void PlayersMonthlyLoop()