src/economy.cpp
branchnoai
changeset 9629 66dde6412125
parent 9628 b5c2449616b5
child 9631 8a2d1c2ceb88
--- a/src/economy.cpp	Sun Jun 17 21:31:00 2007 +0000
+++ b/src/economy.cpp	Tue Jun 26 23:40:58 2007 +0000
@@ -57,10 +57,11 @@
 
 int _score_part[MAX_PLAYERS][SCORE_END];
 
-int64 CalculateCompanyValue(const Player* p)
+Money CalculateCompanyValue(const Player* p)
 {
 	PlayerID owner = p->index;
-	int64 value;
+	/* Do a little nasty by using CommandCost, so we can use the "overflow" protection of CommandCost */
+	CommandCost value;
 
 	{
 		Station *st;
@@ -70,7 +71,7 @@
 			if (st->owner == owner) num += CountBitsSet(st->facilities);
 		}
 
-		value = num * _price.station_value * 25;
+		value.AddCost(num * _price.station_value * 25);
 	}
 
 	{
@@ -83,14 +84,16 @@
 					v->type == VEH_ROAD ||
 					(v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) ||
 					v->type == VEH_SHIP) {
-				value += v->value * 3 >> 1;
+				value.AddCost(v->value * 3 >> 1);
 			}
 		}
 	}
 
-	value += p->money64 - p->current_loan; // add real money value
+	/* Add real money value */
+	value.AddCost(-p->current_loan);
+	value.AddCost(p->player_money);
 
-	return max(value, 1LL);
+	return max(value.GetCost(), 1LL);
 }
 
 /** if update is set to true, the economy is updated with this score
@@ -109,7 +112,7 @@
 /* Count vehicles */
 	{
 		Vehicle *v;
-		int32 min_profit = 0;
+		Money min_profit = 0;
 		bool min_profit_first = true;
 		uint num = 0;
 
@@ -132,7 +135,7 @@
 		_score_part[owner][SCORE_VEHICLES] = num;
 		/* Don't allow negative min_profit to show */
 		if (min_profit > 0)
-			_score_part[owner][SCORE_MIN_PROFIT] = min_profit;
+			_score_part[owner][SCORE_MIN_PROFIT] = ClampToI32(min_profit);
 	}
 
 /* Count stations */
@@ -148,25 +151,21 @@
 
 /* Generate statistics depending on recent income statistics */
 	{
-		const PlayerEconomyEntry* pee;
-		int numec;
-		int32 min_income;
-		int32 max_income;
+		int numec = min(p->num_valid_stat_ent, 12);
+		if (numec != 0) {
+			const PlayerEconomyEntry *pee = p->old_economy;
+			Money min_income = pee->income + pee->expenses;
+			Money max_income = pee->income + pee->expenses;
 
-		numec = min(p->num_valid_stat_ent, 12);
-		if (numec != 0) {
-			min_income = 0x7FFFFFFF;
-			max_income = 0;
-			pee = p->old_economy;
 			do {
 				min_income = min(min_income, pee->income + pee->expenses);
 				max_income = max(max_income, pee->income + pee->expenses);
 			} while (++pee,--numec);
 
 			if (min_income > 0)
-				_score_part[owner][SCORE_MIN_INCOME] = min_income;
+				_score_part[owner][SCORE_MIN_INCOME] = ClampToI32(min_income);
 
-			_score_part[owner][SCORE_MAX_INCOME] = max_income;
+			_score_part[owner][SCORE_MAX_INCOME] = ClampToI32(max_income);
 		}
 	}
 
@@ -197,15 +196,14 @@
 
 /* Generate score for player money */
 	{
-		int32 money = p->player_money;
-		if (money > 0) {
-			_score_part[owner][SCORE_MONEY] = money;
+		if (p->player_money > 0) {
+			_score_part[owner][SCORE_MONEY] = ClampToI32(p->player_money);
 		}
 	}
 
 /* Generate score for loan */
 	{
-		_score_part[owner][SCORE_LOAN] = _score_info[SCORE_LOAN].needed - p->current_loan;
+		_score_part[owner][SCORE_LOAN] = ClampToI32(_score_info[SCORE_LOAN].needed - p->current_loan);
 	}
 
 	/* Now we calculate the score for each item.. */
@@ -259,7 +257,7 @@
 			for (i = 0; i < 4; i++) {
 				if (p->share_owners[i] == old_player) {
 					/* Sell his shares */
-					int32 res = DoCommand(0, p->index, 0, DC_EXEC, CMD_SELL_SHARE_IN_COMPANY);
+					CommandCost res = DoCommand(0, p->index, 0, DC_EXEC, CMD_SELL_SHARE_IN_COMPANY);
 					/* Because we are in a DoCommand, we can't just execute an other one and
 					 *  expect the money to be removed. We need to do it ourself! */
 					SubtractMoneyFromPlayer(res);
@@ -273,7 +271,7 @@
 			_current_player = p->share_owners[i];
 			if (_current_player != PLAYER_SPECTATOR) {
 				/* Sell the shares */
-				int32 res = DoCommand(0, old_player, 0, DC_EXEC, CMD_SELL_SHARE_IN_COMPANY);
+				CommandCost res = DoCommand(0, old_player, 0, DC_EXEC, CMD_SELL_SHARE_IN_COMPANY);
 				/* Because we are in a DoCommand, we can't just execute an other one and
 				 *  expect the money to be removed. We need to do it ourself! */
 				SubtractMoneyFromPlayer(res);
@@ -287,8 +285,7 @@
 	 * removing his/her property doesn't fail because of lack of money.
 	 * Not too drastically though, because it could overflow */
 	if (new_player == PLAYER_SPECTATOR) {
-		GetPlayer(old_player)->money64 = MAX_UVALUE(uint64) >>2; // jackpot ;p
-		UpdatePlayerMoney32(GetPlayer(old_player));
+		GetPlayer(old_player)->player_money = MAX_UVALUE(uint64) >> 2; // jackpot ;p
 	}
 
 	if (new_player == PLAYER_SPECTATOR) {
@@ -440,7 +437,6 @@
 static void PlayersCheckBankrupt(Player *p)
 {
 	PlayerID owner;
-	int64 val;
 
 	/*  If the player has money again, it does not go bankrupt */
 	if (p->player_money >= 0) {
@@ -468,7 +464,7 @@
 
 			/* Check if the company has any value.. if not, declare it bankrupt
 			 *  right now */
-			val = CalculateCompanyValue(p);
+			Money val = CalculateCompanyValue(p);
 			if (val > 0) {
 				p->bankrupt_value = val;
 				p->bankrupt_asked = 1 << owner; // Don't ask the owner
@@ -482,8 +478,7 @@
 			DeletePlayerWindows(owner);
 
 			/* Show bankrupt news */
-			SetDParam(0, p->name_1);
-			SetDParam(1, p->name_2);
+			SetDParam(0, p->index);
 			AddNewsItem( (StringID)(owner | NB_BBANKRUPT), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 
 			if (IsHumanPlayer(owner)) {
@@ -519,8 +514,7 @@
 	DrawPlayerFace(p->face, p->player_color, 2, 23);
 	GfxFillRect(3, 23, 3 + 91, 23 + 118, PALETTE_TO_STRUCT_GREY | (1 << USE_COLORTABLE));
 
-	SetDParam(0, p->president_name_1);
-	SetDParam(1, p->president_name_2);
+	SetDParam(0, p->index);
 
 	DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94);
 
@@ -528,8 +522,7 @@
 	case NB_BTROUBLE:
 		DrawStringCentered(w->width>>1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, 0);
 
-		SetDParam(0, p->name_1);
-		SetDParam(1, p->name_2);
+		SetDParam(0, p->index);
 
 		DrawStringMultiCenter(
 			((w->width - 101) >> 1) + 98,
@@ -543,10 +536,9 @@
 
 		DrawStringCentered(w->width>>1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, 0);
 		COPY_IN_DPARAM(0,WP(w,news_d).ni->params, 2);
-		SetDParam(2, p->name_1);
-		SetDParam(3, p->name_2);
+		SetDParam(2, p->index);
 		price = WP(w,news_d).ni->params[2];
-		SetDParam(4, price);
+		SetDParam(3, price);
 		DrawStringMultiCenter(
 			((w->width - 101) >> 1) + 98,
 			90,
@@ -567,9 +559,8 @@
 
 	case NB_BNEWCOMPANY:
 		DrawStringCentered(w->width>>1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, 0);
-		SetDParam(0, p->name_1);
-		SetDParam(1, p->name_2);
-		COPY_IN_DPARAM(2,WP(w,news_d).ni->params, 2);
+		SetDParam(0, p->index);
+		COPY_IN_DPARAM(1,WP(w,news_d).ni->params, 2);
 		DrawStringMultiCenter(
 			((w->width - 101) >> 1) + 98,
 			90,
@@ -590,16 +581,14 @@
 	case NB_BTROUBLE:
 		SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE);
 		SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED);
-		SetDParam(2, p->name_1);
-		SetDParam(3, p->name_2);
+		SetDParam(2, p->index);
 		return STR_02B6;
 	case NB_BMERGER:
 		SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER);
 		SetDParam(1, STR_705A_HAS_BEEN_SOLD_TO_FOR);
 		COPY_IN_DPARAM(2,ni->params, 2);
-		SetDParam(4, p->name_1);
-		SetDParam(5, p->name_2);
-		COPY_IN_DPARAM(6,ni->params + 2, 1);
+		SetDParam(4, p->index);
+		COPY_IN_DPARAM(5,ni->params + 2, 1);
 		return STR_02B6;
 	case NB_BBANKRUPT:
 		SetDParam(0, STR_705C_BANKRUPT);
@@ -609,9 +598,8 @@
 	case NB_BNEWCOMPANY:
 		SetDParam(0, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED);
 		SetDParam(1, STR_705F_STARTS_CONSTRUCTION_NEAR);
-		SetDParam(2, p->name_1);
-		SetDParam(3, p->name_2);
-		COPY_IN_DPARAM(4,ni->params, 2);
+		SetDParam(2, p->index);
+		COPY_IN_DPARAM(3,ni->params, 2);
 		return STR_02B6;
 	default:
 		NOT_REACHED();
@@ -658,11 +646,17 @@
 	InvalidateWindow(WC_COMPANY_LEAGUE, 0);
 }
 
-static void AddSingleInflation(int32 *value, uint16 *frac, int32 amt)
+static void AddSingleInflation(Money *value, uint16 *frac, int32 amt)
 {
-	int64 tmp = (int64)*value * amt + *frac;
-	*frac   = GB(tmp, 0, 16);
-	*value += tmp >> 16;
+	/* Is it safe to add inflation ? */
+	if ((INT64_MAX / amt) < (*value + 1)) {
+		*value = INT64_MAX / amt;
+		*frac = 0;
+	} else {
+		int64 tmp = (int64)*value * amt + *frac;
+		*frac   = GB(tmp, 0, 16);
+		*value += tmp >> 16;
+	}
 }
 
 static void AddInflation()
@@ -672,10 +666,10 @@
 	 * 12 -> months per year
 	 * This is only a good approxiamtion for small values
 	 */
-	int32 inf = _economy.infl_amount * 54;
+	Money inf = _economy.infl_amount * 54;
 
 	for (uint i = 0; i != NUM_PRICES; i++) {
-		AddSingleInflation((int32*)&_price + i, _price_frac + i, inf);
+		AddSingleInflation((Money*)&_price + i, _price_frac + i, inf);
 	}
 
 	_economy.max_loan_unround += BIGMULUS(_economy.max_loan_unround, inf, 16);
@@ -686,7 +680,7 @@
 	inf = _economy.infl_amount_pr * 54;
 	for (CargoID i = 0; i < NUM_CARGO; i++) {
 		AddSingleInflation(
-			(int32*)_cargo_payment_rates + i,
+			(Money*)_cargo_payment_rates + i,
 			_cargo_payment_rates_frac + i,
 			inf
 		);
@@ -709,7 +703,7 @@
 		_current_player = p->index;
 		SET_EXPENSES_TYPE(EXPENSES_LOAN_INT);
 
-		SubtractMoneyFromPlayer(BIGMULUS(p->current_loan, interest, 16));
+		SubtractMoneyFromPlayer(CommandCost((Money)BIGMULUS(p->current_loan, interest, 16)));
 
 		SET_EXPENSES_TYPE(EXPENSES_OTHER);
 		SubtractMoneyFromPlayer(_price.station_value >> 2);
@@ -739,7 +733,7 @@
 	2,
 };
 
-static const int32 _price_base[NUM_PRICES] = {
+static const Money _price_base[NUM_PRICES] = {
 	    100, ///< station_value
 	    100, ///< build_rail
 	     95, ///< build_road
@@ -822,10 +816,10 @@
 {
 	int i;
 
-	assert(sizeof(_price) == NUM_PRICES * sizeof(int32));
+	assert(sizeof(_price) == NUM_PRICES * sizeof(Money));
 
 	for (i = 0; i != NUM_PRICES; i++) {
-		int32 price = _price_base[i];
+		Money price = _price_base[i];
 		if (_price_category[i] != 0) {
 			uint mod = _price_category[i] == 1 ? _opt.diff.vehicle_costs : _opt.diff.construction_cost;
 			if (mod < 1) {
@@ -839,7 +833,7 @@
 		} else {
 			price >>= 8 - price_base_multiplier[i];
 		}
-		((int32*)&_price)[i] = price;
+		((Money*)&_price)[i] = price;
 		_price_frac[i] = 0;
 	}
 
@@ -1152,7 +1146,7 @@
 		SlObject(&_subsidies[index], _subsidies_desc);
 }
 
-int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type)
+Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type)
 {
 	const CargoSpec *cs = GetCargo(cargo_type);
 	byte f;
@@ -1299,8 +1293,7 @@
 			InjectDParam(2);
 
 			p = GetPlayer(_current_player);
-			SetDParam(0, p->name_1);
-			SetDParam(1, p->name_2);
+			SetDParam(0, p->index);
 			AddNewsItem(
 				STR_2031_SERVICE_SUBSIDY_AWARDED + _opt.diff.subsidy_multiplier,
 				NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0),
@@ -1314,11 +1307,11 @@
 	return false;
 }
 
-static int32 DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, TileIndex source_tile, byte days_in_transit)
+static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, TileIndex source_tile, byte days_in_transit)
 {
 	bool subsidised;
 	Station *s_from, *s_to;
-	int32 profit;
+	Money profit;
 
 	assert(num_pieces > 0);
 
@@ -1368,14 +1361,9 @@
 {
 	int result = 0;
 
-	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
+	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);
@@ -1392,75 +1380,70 @@
 
 	for (Vehicle *v = front_v; v != NULL; v = v->next) {
 		/* No cargo to unload */
-		if (v->cargo_cap == 0 || v->cargo_count == 0) continue;
+		if (v->cargo_cap == 0 || v->cargo.Empty()) continue;
 
 		/* All cargo has already been paid for, no need to pay again */
-		if (v->cargo_count == v->cargo_paid_for) {
+		if (!v->cargo.UnpaidCargo()) {
 			SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
 			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;
+		const CargoList::List *cargos = v->cargo.Packets();
 
-			SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
-		} 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);
+		for (CargoList::List::const_iterator it = cargos->begin(); it != cargos->end(); it++) {
+			CargoPacket *cp = *it;
+			if (!cp->paid_for &&
+					cp->source != last_visited &&
+					ge->acceptance &&
+					(front_v->current_order.flags & OF_TRANSFER) == 0) {
+				/* Deliver goods to the station */
+				st->time_since_unload = 0;
 
-				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
+				/* handle end of route payment */
+				Money profit = DeliverGoods(cp->count, v->cargo_type, cp->source, last_visited, cp->source_xy, cp->days_in_transit);
+				cp->paid_for = true;
+				route_profit   += profit - cp->feeder_share; // display amount paid for final route delivery, A-D of a chain A-B-C-D
+				vehicle_profit += profit;                    // whole vehicle is not payed for transfers picked up earlier
 
-				/* 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);
+				result |= 1;
 
-				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
+				SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
+			} else if (front_v->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) {
+				if (!cp->paid_for && (front_v->current_order.flags & OF_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;
+					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;
+
+				SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
 			}
-			result |= 2;
-
-			SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
 		}
+		v->cargo.InvalidateCache();
 	}
 
-	/* 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 (virtual_profit_total > 0) {
-		ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit_total);
+	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 += total_veh_profit;
+		front_v->profit_this_year += vehicle_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);
+		ShowCostOrIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, -vehicle_profit);
 	}
 
 	_current_player = old_player;
@@ -1483,7 +1466,7 @@
 		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;
+				if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo.Count();
 			}
 		}
 		return;
@@ -1499,14 +1482,13 @@
 	int unloading_time = 0;
 	Vehicle *u = v;
 	int result = 0;
-	int cap;
+	uint cap;
 
 	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;
 
@@ -1523,36 +1505,19 @@
 		}
 
 		GoodsEntry *ge = &st->goods[v->cargo_type];
-		int count = GB(ge->waiting_acceptance, 0, 12);
 
 		if (HASBIT(v->vehicle_flags, VF_CARGO_UNLOADING)) {
-			uint16 amount_unloaded = _patches.gradual_loading ? min(v->cargo_count, load_amount) : v->cargo_count;
+			uint cargo_count = v->cargo.Count();
+			uint amount_unloaded = _patches.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
+			bool remaining; // Are there cargo entities in this vehicle that can still be unloaded here?
 
-			if (v->cargo_source != last_visited && ge->waiting_acceptance & 0x8000 && !(u->current_order.flags & OF_TRANSFER)) {
+			if (ge->acceptance && !(u->current_order.flags & OF_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);
+
 				result |= 1;
 			} else if (u->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) {
-				if (count == 0) {
-					/* No goods waiting at station */
-					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_xy = v->cargo_source_xy;
-					}
-				}
-				/* 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));
+				remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded);
 
 				result |= 2;
 			} else {
@@ -1567,11 +1532,8 @@
 
 			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) {
+			if (_patches.gradual_loading && remaining) {
 				completely_empty = false;
 			} else {
 				/* We have finished unloading (cargo count == 0) */
@@ -1581,9 +1543,6 @@
 			continue;
 		}
 
-		/* We cannot have paid for more cargo than there is on board. */
-		assert(v->cargo_paid_for <= v->cargo_count);
-
 		/* Do not pick up goods that we unloaded */
 		if (u->current_order.flags & OF_UNLOAD) continue;
 
@@ -1596,14 +1555,14 @@
 		}
 
 		/* 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;
+		ge->last_age = _cur_year - u->build_year;
 
 		/* If there's goods waiting at the station, and the vehicle
 		 * has capacity for it, load it on the vehicle. */
-		if (count != 0 &&
-				(cap = v->cargo_cap - v->cargo_count) != 0) {
+		if (!ge->cargo.Empty() &&
+				(cap = v->cargo_cap - v->cargo.Count()) != 0) {
+			uint count = ge->cargo.Count();
 
 			/* Skip loading this vehicle if another train/vehicle is already handling
 			 * the same cargo type at this station */
@@ -1616,11 +1575,11 @@
 			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);
+				cap = min((uint)cargo_left[v->cargo_type], cap);
 				cargo_left[v->cargo_type] -= cap;
 			}
 
-			if (v->cargo_count == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
+			if (v->cargo.Empty()) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
 
 			/* TODO: Regarding this, when we do gradual loading, we
 			 * should first unload all vehicles and then start
@@ -1632,32 +1591,17 @@
 			completely_empty = false;
 			anything_loaded = true;
 
-			/* 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.
-			 * ge->unload_pending holds the amount that has been credited, but has not yet been unloaded.
-			 */
-			int cargoshare = cap * 10000 / (ge->waiting_acceptance + ge->unload_pending);
-			int feeder_profit_share = ge->feeder_profit * cargoshare / 10000;
-			v->cargo_count += cap;
-			ge->waiting_acceptance -= cap;
+			ge->cargo.MoveTo(&v->cargo, cap, CargoList::MTA_CARGO_LOAD, st->xy);
+			ge->days_since_pickup = 0;
 
-			total_cargo_feeder_share += feeder_profit_share;    // store cost for later payment when cargo unloaded
-			v->cargo_loaded_at_xy = st->xy;                     // retains location of where the cargo was picked up for intermediate payments
-
-			ge->feeder_profit -= feeder_profit_share;
 			unloading_time += cap;
 			st->time_since_load = 0;
+			st->last_vehicle_type = v->type;
 
-			/* And record the source of the cargo, and the days in travel. */
-			v->cargo_source = ge->enroute_from;
-			v->cargo_source_xy = ge->enroute_from_xy;
-			v->cargo_days = ge->enroute_time;
 			result |= 2;
-			st->last_vehicle_type = v->type;
 		}
 
-		if (v->cargo_count == v->cargo_cap) {
+		if (v->cargo.Count() == v->cargo_cap) {
 			SETBIT(cargo_full, v->cargo_type);
 		} else {
 			SETBIT(cargo_not_full, v->cargo_type);
@@ -1671,14 +1615,12 @@
 	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;
+			if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo.Count();
 		}
 	}
 
 	v = u;
 
-	v->cargo_feeder_share += total_cargo_feeder_share;
-
 	if (anything_loaded || anything_unloaded) {
 		if (_patches.gradual_loading) {
 			/* The time it takes to load one 'slice' of cargo or passengers depends
@@ -1693,7 +1635,7 @@
 			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) ||
+				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;
 				}
@@ -1715,6 +1657,17 @@
 		}
 	}
 
+	/* Calculate the loading indicator fill percent and display */
+	if (_patches.loading_indicators && _game_mode != GM_MENU && v->owner == _local_player) {
+		StringID percent_up_down = STR_NULL;
+		int percent = CalcPercentVehicleFilled(v, &percent_up_down);
+		if (v->fill_percent_te_id == INVALID_TE_ID) {
+			v->fill_percent_te_id = ShowFillingPercent(v->x_pos, v->y_pos, v->z_pos + 20, percent, percent_up_down);
+		} else {
+			UpdateFillingPercent(v->fill_percent_te_id, percent, percent_up_down);
+		}
+	}
+
 	v->load_unload_time_rem = unloading_time;
 
 	if (completely_empty) {
@@ -1741,7 +1694,7 @@
 {
 	int cargo_left[NUM_CARGO];
 
-	for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = GB(st->goods[i].waiting_acceptance, 0, 12);
+	for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count();
 
 	std::list<Vehicle *>::iterator iter;
 	for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
@@ -1766,11 +1719,10 @@
 {
 	Player *owner;
 	int i;
-	int64 value;
+	Money value;
 
-	SetDParam(0, p->name_1);
-	SetDParam(1, p->name_2);
-	SetDParam(2, p->bankrupt_value);
+	SetDParam(0, p->index);
+	SetDParam(1, p->bankrupt_value);
 	AddNewsItem( (StringID)(_current_player | NB_BMERGER), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 
 	/* original code does this a little bit differently */
@@ -1784,14 +1736,15 @@
 	}
 
 	value = CalculateCompanyValue(p) >> 2;
+	PlayerID old_player = _current_player;
 	for (i = 0; i != 4; i++) {
 		if (p->share_owners[i] != PLAYER_SPECTATOR) {
-			owner = GetPlayer(p->share_owners[i]);
-			owner->money64 += value;
-			owner->yearly_expenses[0][EXPENSES_OTHER] += value;
-			UpdatePlayerMoney32(owner);
+			SET_EXPENSES_TYPE(EXPENSES_OTHER);
+			_current_player = p->share_owners[i];
+			SubtractMoneyFromPlayer(CommandCost(-value));
 		}
 	}
+	_current_player = old_player;
 
 	p->is_active = false;
 
@@ -1807,10 +1760,10 @@
  * @param p1 player to buy the shares from
  * @param p2 unused
  */
-int32 CmdBuyShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdBuyShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	Player *p;
-	int64 cost;
+	CommandCost cost;
 
 	/* Check if buying shares is allowed (protection against modified clients */
 	if (!IsValidPlayer((PlayerID)p1) || !_patches.allow_shares) return CMD_ERROR;
@@ -1822,12 +1775,12 @@
 	if (_cur_year - p->inaugurated_year < 6) return_cmd_error(STR_7080_PROTECTED);
 
 	/* Those lines are here for network-protection (clients can be slow) */
-	if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 0) return 0;
+	if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 0) return cost;
 
 	/* We can not buy out a real player (temporarily). TODO: well, enable it obviously */
-	if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 1 && !p->is_ai) return 0;
+	if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 1 && !p->is_ai) return cost;
 
-	cost = CalculateCompanyValue(p) >> 2;
+	cost.AddCost(CalculateCompanyValue(p) >> 2);
 	if (flags & DC_EXEC) {
 		PlayerByte* b = p->share_owners;
 		int i;
@@ -1853,10 +1806,10 @@
  * @param p1 player to sell the shares from
  * @param p2 unused
  */
-int32 CmdSellShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdSellShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	Player *p;
-	int64 cost;
+	Money cost;
 
 	/* Check if buying shares is allowed (protection against modified clients */
 	if (!IsValidPlayer((PlayerID)p1) || !_patches.allow_shares) return CMD_ERROR;
@@ -1865,7 +1818,7 @@
 	p = GetPlayer((PlayerID)p1);
 
 	/* Those lines are here for network-protection (clients can be slow) */
-	if (GetAmountOwnedBy(p, _current_player) == 0) return 0;
+	if (GetAmountOwnedBy(p, _current_player) == 0) return CommandCost();
 
 	/* adjust it a little to make it less profitable to sell and buy */
 	cost = CalculateCompanyValue(p) >> 2;
@@ -1877,7 +1830,7 @@
 		*b = PLAYER_SPECTATOR;
 		InvalidateWindow(WC_COMPANY, p1);
 	}
-	return cost;
+	return CommandCost(cost);
 }
 
 /** Buy up another company.
@@ -1889,7 +1842,7 @@
  * @param p1 player/company to buy up
  * @param p2 unused
  */
-int32 CmdBuyCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdBuyCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	Player *p;
 	PlayerID pid = (PlayerID)p1;
@@ -1908,13 +1861,14 @@
 	if (flags & DC_EXEC) {
 		DoAcquireCompany(p);
 	}
-	return p->bankrupt_value;
+	return CommandCost(p->bankrupt_value);
 }
 
 /** Prices */
 static void SaveLoad_PRIC()
 {
-	SlArray(&_price,      NUM_PRICES, SLE_INT32);
+	int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64;
+	SlArray(&_price,      NUM_PRICES, vt);
 	SlArray(&_price_frac, NUM_PRICES, SLE_UINT16);
 }
 
@@ -1922,18 +1876,21 @@
 static void SaveLoad_CAPR()
 {
 	uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO;
-	SlArray(&_cargo_payment_rates,      num_cargo, SLE_INT32);
+	int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64;
+	SlArray(&_cargo_payment_rates,      num_cargo, vt);
 	SlArray(&_cargo_payment_rates_frac, num_cargo, SLE_UINT16);
 }
 
 static const SaveLoad _economy_desc[] = {
-	SLE_VAR(Economy, max_loan,         SLE_INT32),
-	SLE_VAR(Economy, max_loan_unround, SLE_INT32),
-	SLE_VAR(Economy, fluct,            SLE_FILE_I16 | SLE_VAR_I32),
-	SLE_VAR(Economy, interest_rate,    SLE_UINT8),
-	SLE_VAR(Economy, infl_amount,      SLE_UINT8),
-	SLE_VAR(Economy, infl_amount_pr,   SLE_UINT8),
-	SLE_END()
+	SLE_CONDVAR(Economy, max_loan,         SLE_FILE_I32 | SLE_VAR_I64,  0, 64),
+	SLE_CONDVAR(Economy, max_loan,         SLE_INT64,                  65, SL_MAX_VERSION),
+	SLE_CONDVAR(Economy, max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64,  0, 64),
+	SLE_CONDVAR(Economy, max_loan_unround, SLE_INT64,                  65, SL_MAX_VERSION),
+	    SLE_VAR(Economy, fluct,            SLE_FILE_I16 | SLE_VAR_I32),
+	    SLE_VAR(Economy, interest_rate,    SLE_UINT8),
+	    SLE_VAR(Economy, infl_amount,      SLE_UINT8),
+	    SLE_VAR(Economy, infl_amount_pr,   SLE_UINT8),
+	    SLE_END()
 };
 
 /** Economy variables */