economy.c
author Darkvater
Mon, 30 May 2005 22:16:05 +0000
changeset 1881 023a134a4b12
parent 1797 a19c4181f1b7
child 1884 ae1d6213c6dd
permissions -rw-r--r--
(svn r2387) - CodeChange: made the saveload code more readable and also removed the 'byte' saveload arrays which means you can save an array of more than 255 elements, or bigger structs than 255 bytes. This doesn't yet solve the problem that a chunk can be a maximum of 16384 big.
- Fix: also fix an unnoticed error in SlSaveLoadConv() due to wrong types.
#include "stdafx.h"
#include "ttd.h"
#include "strings.h" // XXX InjectDParam()
#include "table/strings.h"
#include "map.h"
#include "news.h"
#include "player.h"
#include "station.h"
#include "vehicle.h"
#include "window.h"
#include "gfx.h"
#include "command.h"
#include "saveload.h"
#include "economy.h"
#include "industry.h"
#include "town.h"
#include "network.h"
#include "sound.h"
#include "engine.h"
#include "network_data.h"

// get a mask of the allowed currencies depending on the year
uint GetMaskOfAllowedCurrencies(void)
{
	int i;
	uint mask = 0;
	for (i = 0; i != lengthof(_currency_specs); i++) {
		uint16 to_euro = _currency_specs[i].to_euro;
		if (i == 23) mask |= (1 << 23); // always allow custom currency
		if (to_euro != CF_NOEURO && to_euro != CF_ISEURO && _cur_year >= (to_euro-MAX_YEAR_BEGIN_REAL)) continue;
		if (_cur_year < (2000-MAX_YEAR_BEGIN_REAL) && (to_euro == CF_ISEURO)) continue;
		mask |= (1 << i);
	}
	return mask;
}

void CheckSwitchToEuro(void)
{
	if (_currency_specs[_opt.currency].to_euro != CF_NOEURO &&
			_currency_specs[_opt.currency].to_euro != CF_ISEURO &&
			_cur_year >= (_currency_specs[_opt.currency].to_euro-MAX_YEAR_BEGIN_REAL)) {
		_opt.currency = 2; // this is the index of euro above.
		AddNewsItem(STR_EURO_INTRODUCE, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0);
	}
}

void UpdatePlayerHouse(Player *p, uint score)
{
	byte val;
	uint tile = p->location_of_house;

	if (tile == 0)
		return;

	(val = 128, score < 170) ||
	(val+= 4, score < 350) ||
	(val+= 4, score < 520) ||
	(val+= 4, score < 720) ||
	(val+= 4, true);

/* house is already big enough */
	if (val <= _map5[tile])
		return;

	_map5[tile + TILE_XY(0,0)] = val;
	_map5[tile + TILE_XY(0,1)] = ++val;
	_map5[tile + TILE_XY(1,0)] = ++val;
	_map5[tile + TILE_XY(1,1)] = ++val;

	MarkTileDirtyByTile(tile + TILE_XY(0,0));
	MarkTileDirtyByTile(tile + TILE_XY(0,1));
	MarkTileDirtyByTile(tile + TILE_XY(1,0));
	MarkTileDirtyByTile(tile + TILE_XY(1,1));
}

int64 CalculateCompanyValue(Player *p) {
	byte owner = p->index;
	int64 value;

	{
		Station *st;
		uint num = 0;

		FOR_ALL_STATIONS(st) {
			if (st->xy != 0 && st->owner == owner) {
				uint facil = st->facilities;
				do num += (facil&1); while (facil >>= 1);
			}
		}

		value = num * _price.station_value * 25;
	}

	{
		Vehicle *v;

		FOR_ALL_VEHICLES(v) {
			if (v->owner != owner)
				continue;
			if (v->type == VEH_Train ||
					v->type == VEH_Road ||
					(v->type == VEH_Aircraft && v->subtype<=2) ||
					v->type == VEH_Ship) {
				value += v->value * 3 >> 1;
			}
		}
	}

	value += p->money64 - p->current_loan; // add real money value

	return max64(value, 1);
}

// if update is set to true, the economy is updated with this score
//  (also the house is updated, should only be true in the on-tick event)
int UpdateCompanyRatingAndValue(Player *p, bool update)
{
	byte owner = p->index;
	int score = 0;

	memset(_score_part[owner], 0, sizeof(_score_part[owner]));

/* Count vehicles */
	{
		Vehicle *v;
		int32 min_profit = score_info[SCORE_MIN_PROFIT].needed;
		uint num = 0;

		FOR_ALL_VEHICLES(v) {
			if (v->owner != owner)
				continue;
			if ((v->type == VEH_Train && v->subtype == TS_Front_Engine) ||
					v->type == VEH_Road ||
					(v->type == VEH_Aircraft && v->subtype<=2) ||
					v->type == VEH_Ship) {
				num++;
				if (v->age > 730) {
					if (min_profit > v->profit_last_year)
						min_profit = v->profit_last_year;
				}
			}
		}

		_score_part[owner][SCORE_VEHICLES] = num;
		if (min_profit > 0)
			_score_part[owner][SCORE_MIN_PROFIT] = min_profit;
	}

/* Count stations */
	{
		uint num = 0;
		Station *st;

		FOR_ALL_STATIONS(st) {
			if (st->xy != 0 && st->owner == owner) {
				int facil = st->facilities;
				do num += facil&1; while (facil>>=1);
			}
		}
		_score_part[owner][SCORE_STATIONS] = num;
	}

/* Generate statistics depending on recent income statistics */
	{
		PlayerEconomyEntry *pee;
		int numec;
		int32 min_income;
		int32 max_income;

		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_MAX_INCOME] = max_income;
		}
	}

/* Generate score depending on amount of transported cargo */
	{
		PlayerEconomyEntry *pee;
		int numec;
		uint32 total_delivered;

		numec = min(p->num_valid_stat_ent, 4);
		if (numec != 0) {
			pee = p->old_economy;
			total_delivered = 0;
			do {
				total_delivered += pee->delivered_cargo;
			} while (++pee,--numec);

			_score_part[owner][SCORE_DELIVERED] = total_delivered;
		}
	}

/* Generate score for variety of cargo */
	{
		uint cargo = p->cargo_types;
		uint num = 0;
		do num += cargo&1; while (cargo>>=1);
		_score_part[owner][SCORE_CARGO] = num;
		if (update)
			p->cargo_types = 0;
	}

/* Generate score for player money */
	{
		int32 money = p->player_money;
		if (money > 0) {
			_score_part[owner][SCORE_MONEY] = money;
		}
	}

/* Generate score for loan */
	{
		_score_part[owner][SCORE_LOAN] = score_info[SCORE_LOAN].needed - p->current_loan;
	}

	// Now we calculate the score for each item..
	{
		int i;
		int total_score = 0;
		int s;
		score = 0;
		for (i=0;i<NUM_SCORE;i++) {
			// Skip the total
			if (i == SCORE_TOTAL) continue;
			// Check the score
			s = (_score_part[owner][i] >= score_info[i].needed) ? score_info[i].score : ((_score_part[owner][i] * score_info[i].score) / score_info[i].needed);
			if (s < 0) s = 0;
			score += s;
			total_score += score_info[i].score;
		}

		_score_part[owner][SCORE_TOTAL] = score;

		// We always want the score scaled to SCORE_MAX (1000)
		if (total_score != SCORE_MAX)
			score = score * SCORE_MAX / total_score;
	}

	if (update) {
    	p->old_economy[0].performance_history = score;
    	UpdatePlayerHouse(p, score);
    	p->old_economy[0].company_value = CalculateCompanyValue(p);
    }

	InvalidateWindow(WC_PERFORMANCE_DETAIL, 0);
	return score;
}

// use 255 as new_player to delete the player.
void ChangeOwnershipOfPlayerItems(PlayerID old_player, PlayerID new_player)
{
	PlayerID old = _current_player;
	_current_player = old_player;

	if (new_player == OWNER_SPECTATOR) {
		Subsidy *s;

		for (s = _subsidies; s != endof(_subsidies); s++) {
			if (s->cargo_type != 0xff && s->age >= 12) {
				if (GetStation(s->to)->owner == old_player)
					s->cargo_type = 0xff;
			}
		}
	}

	/* Take care of rating in towns */
	{ Town *t;
		if (new_player != OWNER_SPECTATOR) {
			FOR_ALL_TOWNS(t) {
				/* If a player takes over, give the ratings to that player. */
				if (IsValidTown(t) && HASBIT(t->have_ratings, old_player)) {
					if (HASBIT(t->have_ratings, new_player)) {
						// use max of the two ratings.
						t->ratings[new_player] = max(t->ratings[new_player], t->ratings[old_player]);
					} else {
						SETBIT(t->have_ratings, new_player);
						t->ratings[new_player] = t->ratings[old_player];
					}
				}

				/* Reset ratings for the town */
				if (IsValidTown(t)) {
					t->ratings[old_player] = 500;
					CLRBIT(t->have_ratings, old_player);
				}
			}
		}
	}

	{
		int num_train = 0;
		int num_road = 0;
		int num_ship = 0;
		int num_aircraft = 0;
		Vehicle *v;

		// Determine Ids for the new vehicles
		FOR_ALL_VEHICLES(v) {
			if (v->owner == new_player) {
				switch (v->type) {
					case VEH_Train:
						if (v->subtype == TS_Front_Engine) num_train++;
						break;
					case VEH_Road:
						num_road++;
						break;
					case VEH_Ship:
						num_ship++;
						break;
					case VEH_Aircraft:
						if (v->subtype <= 2) num_aircraft++;
						break;
					default: break;
				}
			}
		}

		FOR_ALL_VEHICLES(v) {
			if (v->owner == old_player && IS_BYTE_INSIDE(v->type, VEH_Train, VEH_Aircraft+1) ) {
				if (new_player == OWNER_SPECTATOR) {
					DeleteWindowById(WC_VEHICLE_VIEW, v->index);
					DeleteWindowById(WC_VEHICLE_DETAILS, v->index);
					DeleteWindowById(WC_VEHICLE_ORDERS, v->index);
					DeleteVehicle(v);
				} else {
					v->owner = new_player;
					if (v->type == VEH_Train && v->subtype == TS_Front_Engine)
						v->unitnumber = ++num_train;
					else if (v->type == VEH_Road)
						v->unitnumber = ++num_road;
					else if (v->type == VEH_Ship)
						v->unitnumber = ++num_ship;
					else if (v->type == VEH_Aircraft && v->subtype <= 2)
						v->unitnumber = ++num_aircraft;
				}
			}
		}
	}

	// Change ownership of tiles
	{
		TileIndex tile = 0;
		do {
			ChangeTileOwner(tile, old_player, new_player);
		} while (++tile != MapSize());
	}

	// Change color of existing windows
	if (new_player != OWNER_SPECTATOR) {
		Window *w;
		for (w = _windows; w != _last_window; w++) {
			if (w->caption_color == old_player)
				w->caption_color = new_player;
		}
	}

	{
		Player *p;
		uint i;

		/* Check for shares */
		FOR_ALL_PLAYERS(p) {
			for (i = 0; i < 4; i++) {
				/* 'Sell' the share if this player has any */
				if (p->share_owners[i] == _current_player)
					p->share_owners[i] = 0xFF;
			}
		}
		p = DEREF_PLAYER(_current_player);
		/* Sell all the shares that people have on this company */
		for (i = 0; i < 4; i++)
			p->share_owners[i] = 0xFF;
	}

	_current_player = old;

	MarkWholeScreenDirty();
}

extern void DeletePlayerWindows(int pi);

static void PlayersCheckBankrupt(Player *p)
{
	int owner;
	int64 val;

	// If the player has money again, it does not go bankrupt
	if (p->player_money >= 0) {
		p->quarters_of_bankrupcy = 0;
		return;
	}

	p->quarters_of_bankrupcy++;

	owner = p->index;

	switch (p->quarters_of_bankrupcy) {
		case 2:
			AddNewsItem( (StringID)(owner + 16),
				NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
			break;
		case 3: {
			/* XXX - In multiplayer, should we ask other players if it wants to take
		          over when it is a human company? -- TrueLight */
			if (IS_HUMAN_PLAYER(owner)) {
				AddNewsItem( (StringID)(owner + 16),
					NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
				break;
			}

			// Check if the company has any value.. if not, declare it bankrupt
			//  right now
			val = CalculateCompanyValue(p);
			if (val > 0) {
				p->bankrupt_value = val;
				p->bankrupt_asked = 1 << owner; // Don't ask the owner
				p->bankrupt_timeout = 0;
				break;
			}
			// Else, falltrue to case 4...
		}
		case 4: {
			// Close everything the owner has open
			DeletePlayerWindows(owner);

//		Show bankrupt news
			SetDParam(0, p->name_1);
			SetDParam(1, p->name_2);
			AddNewsItem( (StringID)(owner + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);

			// If the player is human, and it is no network play, leave the player playing
			if (IS_HUMAN_PLAYER(owner) && !_networking) {
				p->bankrupt_asked = 255;
				p->bankrupt_timeout = 0x456;
			} else {
#ifdef ENABLE_NETWORK
				if (IS_HUMAN_PLAYER(owner) && _network_server) {
					// If we are the server, make sure it is clear that his player is no
					//  longer with us!
					NetworkClientInfo *ci;
					NetworkClientState *cs;
					/* Find all clients that were in control of this company */
					FOR_ALL_CLIENTS(cs) {
						ci = DEREF_CLIENT_INFO(cs);
						if ((ci->client_playas-1) == owner) {
							ci->client_playas = OWNER_SPECTATOR;
							// Send the new info to all the clients
							NetworkUpdateClientInfo(_network_own_client_index);
						}
					}
				}
				// Make sure the player no longer controls the company
				if (IS_HUMAN_PLAYER(owner) && owner == _local_player) {
					// Switch the player to spectator..
					_local_player = OWNER_SPECTATOR;
				}
#endif /* ENABLE_NETWORK */

				// Convert everything the player owns to NO_OWNER
				p->money64 = p->player_money = 100000000;
				ChangeOwnershipOfPlayerItems(owner, 0xFF); // 255 is no owner
				// Register the player as not-active
				p->is_active = false;
			}
		}
	}
}

void DrawNewsBankrupcy(Window *w)
{
	Player *p;

	DrawNewsBorder(w);

	p = DEREF_PLAYER(WP(w,news_d).ni->string_id & 15);
	DrawPlayerFace(p->face, p->player_color, 2, 23);
	GfxFillRect(3, 23, 3+91, 23+118, 0x4323);

	SetDParam(0, p->president_name_1);
	SetDParam(1, p->president_name_2);

	DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94);

	switch(WP(w,news_d).ni->string_id >> 4) {
	case 1:
		DrawStringCentered(w->width>>1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, 0);

		SetDParam(0, p->name_1);
		SetDParam(1, p->name_2);

		DrawStringMultiCenter(
			((w->width - 101) >> 1) + 98,
			90,
			STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED,
			w->width - 101);
		break;

	case 2: {
		int32 price;

		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);
		price = WP(w,news_d).ni->params[2];
		SetDParam(4, price);
		DrawStringMultiCenter(
			((w->width - 101) >> 1) + 98,
			90,
			price==0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR,
			w->width - 101);
		break;
	}

	case 3:
		DrawStringCentered(w->width>>1, 1, STR_705C_BANKRUPT, 0);
		COPY_IN_DPARAM(0,WP(w,news_d).ni->params, 2);
		DrawStringMultiCenter(
			((w->width - 101) >> 1) + 98,
			90,
			STR_705D_HAS_BEEN_CLOSED_DOWN_BY,
			w->width - 101);
		break;

	case 4:
		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);
		DrawStringMultiCenter(
			((w->width - 101) >> 1) + 98,
			90,
			STR_705F_STARTS_CONSTRUCTION_NEAR,
			w->width - 101);
		break;

	default:
		NOT_REACHED();
	}
}

StringID GetNewsStringBankrupcy(NewsItem *ni)
{
	Player *p = DEREF_PLAYER(ni->string_id & 0xF);

	switch(ni->string_id >> 4) {
	case 1:
		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);
		return STR_02B6;
	case 2:
		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);
		return STR_02B6;
	case 3:
		SetDParam(0, STR_705C_BANKRUPT);
		SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY);
		COPY_IN_DPARAM(2,ni->params, 2);
		return STR_02B6;
	case 4:
		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);
		return STR_02B6;
	default:
		NOT_REACHED();
	}

	/* useless, but avoids compiler warning this way */
	return 0;
}

static void PlayersGenStatistics(void)
{
	Station *st;
	Player *p;

	FOR_ALL_STATIONS(st) {
		if (st->xy != 0) {
			_current_player = st->owner;
			SET_EXPENSES_TYPE(EXPENSES_PROPERTY);
			SubtractMoneyFromPlayer(_price.station_value >> 1);
		}
	}

	if (!HASBIT(1<<0|1<<3|1<<6|1<<9, _cur_month))
		return;

	FOR_ALL_PLAYERS(p) {
		if (p->is_active) {
			memmove(&p->old_economy, &p->cur_economy, sizeof(p->old_economy));
			memset(&p->cur_economy, 0, sizeof(p->cur_economy));

			if (p->num_valid_stat_ent != 24)
				p->num_valid_stat_ent++;

			UpdateCompanyRatingAndValue(p, true);
			PlayersCheckBankrupt(p);

			if (p->block_preview != 0)
				p->block_preview--;
		}
	}

	InvalidateWindow(WC_INCOME_GRAPH, 0);
	InvalidateWindow(WC_OPERATING_PROFIT, 0);
	InvalidateWindow(WC_DELIVERED_CARGO, 0);
	InvalidateWindow(WC_PERFORMANCE_HISTORY, 0);
	InvalidateWindow(WC_COMPANY_VALUE, 0);
	InvalidateWindow(WC_COMPANY_LEAGUE, 0);
}

static void AddSingleInflation(int32 *value, uint16 *frac, int32 amt)
{
	int64 tmp;
	int32 low;
	tmp = BIGMULS(*value, amt);
	*frac = (uint16)(low = (uint16)tmp + *frac);
	*value += (int32)(tmp >> 16) + (low >> 16);
}

static void AddInflation(void)
{
	int i;
	int32 inf = _economy.infl_amount * 54;

	for(i=0; i!=NUM_PRICES; i++) {
		AddSingleInflation( (int32*)&_price + i, _price_frac + i, inf );
	}

	_economy.max_loan_unround += BIGMULUS(_economy.max_loan_unround, inf, 16);

	if (_economy.max_loan + 50000 <= _economy.max_loan_unround)
		_economy.max_loan += 50000;

	inf = _economy.infl_amount_pr * 54;
	for(i=0; i!=NUM_CARGO; i++) {
		AddSingleInflation(
			_cargo_payment_rates + i,
			_cargo_payment_rates_frac + i,
			inf
		);
	}

	InvalidateWindowClasses(WC_BUILD_VEHICLE);
	InvalidateWindowClasses(WC_REPLACE_VEHICLE);
	InvalidateWindowClasses(WC_VEHICLE_DETAILS);
	InvalidateWindow(WC_PAYMENT_RATES, 0);
}

static void PlayersPayInterest(void)
{
	Player *p;
	int interest = _economy.interest_rate * 54;

	FOR_ALL_PLAYERS(p) {
		if (!p->is_active)
			continue;

		_current_player = p->index;
		SET_EXPENSES_TYPE(EXPENSES_LOAN_INT);

		SubtractMoneyFromPlayer(BIGMULUS(p->current_loan, interest, 16));

		SET_EXPENSES_TYPE(EXPENSES_OTHER);
		SubtractMoneyFromPlayer(_price.station_value >> 2);
	}
}

static void HandleEconomyFluctuations(void)
{
	if (_opt.diff.economy == 0)
		return;

	if (--_economy.fluct == 0) {
		_economy.fluct = - (int)(Random()&3);
		AddNewsItem(STR_7073_WORLD_RECESSION_FINANCIAL, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0);
	} else if (_economy.fluct == -12) {
		_economy.fluct = (Random()&255) + 312;
		AddNewsItem(STR_7074_RECESSION_OVER_UPTURN_IN, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0);
	}
}

static byte _price_category[NUM_PRICES] = {
	0, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 1, 1, 1, 1, 1, 1,
	2,
};

static const int32 _price_base[NUM_PRICES] = {
	100,		// station_value
	100,		// build_rail
	95,			// build_road
	65,			// build_signals
	275,		// build_bridge
	600,		// build_train_depot
	500,		// build_road_depot
	700,		// build_ship_depot
	450,		// build_tunnel
	200,		// train_station_track
	180,		// train_station_length
	600,		// build_airport
	200,		// build_bus_station
	200,		// build_truck_station
	350,		// build_dock
	400000,	// build_railvehicle
	2000,		// build_railwagon
	700000,	// aircraft_base
	14000,	// roadveh_base
	65000,	// ship_base
	20,			// build_trees
	250,		// terraform
	20,			// clear_1
	40,			// purchase_land
	200,		// clear_2
	500,		// clear_3
	20,			// remove_trees
	-70,		// remove_rail
	10,			// remove_signals
	50,			// clear_bridge
	80,			// remove_train_depot
	80,			// remove_road_depot
	90,			// remove_ship_depot
	30,			// clear_tunnel
	10000,	// clear_water
	50,			// remove_rail_station
	30,			// remove_airport
	50,			// remove_bus_station
	50,			// remove_truck_station
	55,			// remove_dock
	1600,		// remove_house
	40,			// remove_road
	5600,		// running_rail[0] railroad
	5200,		// running_rail[1] monorail
	4800,		// running_rail[2] maglev
	9600,		// aircraft_running
	1600,		// roadveh_running
	5600,		// ship_running
	1000000, // build_industry
};

void StartupEconomy(void)
{
	int i;

	assert(sizeof(_price) == NUM_PRICES * sizeof(int32));

	for(i=0; i!=NUM_PRICES; i++) {
		int32 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) {
				price = price * 3 >> 2;
			} else if (mod > 1) {
				price = price * 9 >> 3;
			}
		}
		((int32*)&_price)[i] = price;
		_price_frac[i] = 0;
	}

	_economy.interest_rate = _opt.diff.initial_interest;
	_economy.infl_amount = _opt.diff.initial_interest;
	_economy.infl_amount_pr = max(0, _opt.diff.initial_interest - 1);
	_economy.max_loan_unround = _economy.max_loan = _opt.diff.max_loan * 1000;
	_economy.fluct = (byte)(Random()) + 168;
}

Pair SetupSubsidyDecodeParam(Subsidy *s, bool mode)
{
	int tile, tile2;
	Industry *i;
	Town *t;
	Station *st;
	Pair tp;

	/* if mode is false, convert into plural */
	SetDParam(0, _cargoc.names_s[s->cargo_type] + (mode?0:32));

	if (s->age < 12) {
		if (!(s->cargo_type == CT_PASSENGERS || s->cargo_type == CT_MAIL)) {
			SetDParam(1, STR_2029);
			i = GetIndustry(s->from);
			tile = i->xy;
			SetDParam(2, i->town->townnametype);
			SetDParam(3, i->town->townnameparts);

			SetDParam(4, i->type + STR_4802_COAL_MINE);

			if (s->cargo_type != CT_GOODS && s->cargo_type != CT_FOOD) {
				SetDParam(5, STR_2029);
				i = GetIndustry(s->to);
				tile2 = i->xy;
				SetDParam(8, i->type + STR_4802_COAL_MINE);
				SetDParam(6, i->town->townnametype);
				SetDParam(7, i->town->townnameparts);
			} else {
				t = GetTown(s->to);
				tile2 = t->xy;
				SetDParam(5, t->townnametype);
				SetDParam(6, t->townnameparts);
			}
		} else {
			t = GetTown(s->from);
			tile = t->xy;
			SetDParam(1, t->townnametype);
			SetDParam(2, t->townnameparts);

			t = GetTown(s->to);
			tile2 = t->xy;
			SetDParam(3, t->townnametype);
			SetDParam(4, t->townnameparts);
		}
	} else {
		st = GetStation(s->from);
		tile = st->xy;
		SetDParam(1, st->index);

		st = GetStation(s->to);
		tile2 = st->xy;
		SetDParam(2, st->index);
	}

	tp.a = tile;
	tp.b = tile2;

	return tp;
}

void DeleteSubsidyWithIndustry(uint16 index)
{
	Subsidy *s;

	for(s=_subsidies; s != endof(_subsidies); s++) {
		if (s->cargo_type != 0xFF && s->age < 12 &&
				s->cargo_type != CT_PASSENGERS && s->cargo_type != CT_MAIL &&
				(index == s->from || (s->cargo_type!=CT_GOODS && s->cargo_type!=CT_FOOD && index==s->to))) {
			s->cargo_type = 0xFF;
		}
	}
}

void DeleteSubsidyWithStation(uint16 index)
{
	Subsidy *s;
	bool dirty = false;

	for(s=_subsidies; s != endof(_subsidies); s++) {
		if (s->cargo_type != 0xFF && s->age >= 12 &&
				(s->from == index || s->to == index)) {
			s->cargo_type = 0xFF;
			dirty = true;
		}
	}

	if (dirty)
		InvalidateWindow(WC_SUBSIDIES_LIST, 0);
}

typedef struct FoundRoute {
	uint distance;
	byte cargo;
	void *from;
	void *to;
} FoundRoute;

static void FindSubsidyPassengerRoute(FoundRoute *fr)
{
	Town *from,*to;

	fr->distance = (uint)-1;

	fr->from = from = GetTown(RandomRange(_total_towns));
	if (from->xy == 0 || from->population < 400)
		return;

	fr->to = to = GetTown(RandomRange(_total_towns));
	if (from==to || to->xy == 0 || to->population < 400 || to->pct_pass_transported > 42)
		return;

	fr->distance = DistanceManhattan(from->xy, to->xy);
}

static void FindSubsidyCargoRoute(FoundRoute *fr)
{
	Industry *i;
	int trans, total;
	byte cargo;

	fr->distance = (uint)-1;

	fr->from = i = GetIndustry(RandomRange(_total_industries));
	if (i->xy == 0)
		return;

	// Randomize cargo type
	if (Random()&1 && i->produced_cargo[1] != 0xFF) {
		cargo = i->produced_cargo[1];
		trans = i->pct_transported[1];
		total = i->total_production[1];
	} else {
		cargo = i->produced_cargo[0];
		trans = i->pct_transported[0];
		total = i->total_production[0];
	}

	// Quit if no production in this industry
	//  or if the cargo type is passengers
	//  or if the pct transported is already large enough
	if (total == 0  || trans > 42 || cargo == 0xFF || cargo == CT_PASSENGERS)
		return;

	fr->cargo = cargo;

	if (cargo == CT_GOODS || cargo == CT_FOOD) {
		// The destination is a town
		Town *t = GetTown(RandomRange(_total_towns));

		// Only want big towns
		if (t->xy == 0 || t->population < 900)
			return;
		fr->distance = DistanceManhattan(i->xy, t->xy);
		fr->to = t;
	} else {
		// The destination is an industry
		Industry *i2 = GetIndustry(RandomRange(_total_industries));

		// The industry must accept the cargo
		if (i == i2 || i2->xy == 0 ||
				(cargo != i2->accepts_cargo[0] &&
				cargo != i2->accepts_cargo[1] &&
				cargo != i2->accepts_cargo[2]))
			return;
		fr->distance = DistanceManhattan(i->xy, i2->xy);
		fr->to = i2;
	}
}

static bool CheckSubsidyDuplicate(Subsidy *s)
{
	Subsidy *ss;

	for(ss=_subsidies; ss != endof(_subsidies); ss++) {
		if (s != ss &&
//				ss->age < 12 &&
				ss->from == s->from &&
				ss->to == s->to &&
				ss->cargo_type == s->cargo_type) {
			s->cargo_type = 0xFF;
			return true;
		}
	}
	return false;
}

void RemoteSubsidyAdd(Subsidy *s_new)
{
	Subsidy *s;
	Pair pair;

	// search the first free subsidy
	for(s=_subsidies; s != endof(_subsidies); s++)
		if (s->cargo_type == 0xFF)
			break;

	memcpy(s,s_new,sizeof(Subsidy));

	pair = SetupSubsidyDecodeParam(s, 0);
	AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);

	InvalidateWindow(WC_SUBSIDIES_LIST, 0);
}

static void SubsidyMonthlyHandler(void)
{
	Subsidy *s;
	Pair pair;
	Station *st;
	uint n;
	FoundRoute fr;
	bool modified = false;

	for(s=_subsidies; s != endof(_subsidies); s++) {
		if (s->cargo_type == 0xFF)
			continue;

		if (s->age == 12-1) {
			pair = SetupSubsidyDecodeParam(s, 1);
			AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
			s->cargo_type = 0xFF;
			modified = true;
		} else if (s->age == 2*12-1) {
			st = GetStation(s->to);
			if (st->owner == _local_player) {
				pair = SetupSubsidyDecodeParam(s, 1);
				AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
			}
			s->cargo_type = 0xFF;
			modified = true;
		} else {
			s->age++;
		}
	}

	// 25% chance to go on
	if (CHANCE16(1,4)) {
		// Find a free slot
		s = _subsidies;
		while (s->cargo_type != 0xFF) {
			if (++s == endof(_subsidies))
				goto no_add;
		}

		n = 1000;
		do {
			FindSubsidyPassengerRoute(&fr);
			if (fr.distance <= 70) {
				s->cargo_type = CT_PASSENGERS;
				s->from = ((Town*)fr.from)->index;
				s->to = ((Town*)fr.to)->index;
				goto add_subsidy;
			}
			FindSubsidyCargoRoute(&fr);
			if (fr.distance <= 70) {
				s->cargo_type = fr.cargo;
				s->from = ((Industry*)fr.from)->index;
				s->to = (fr.cargo == CT_GOODS || fr.cargo == CT_FOOD) ? ((Town*)fr.to)->index : ((Industry*)fr.to)->index;
	add_subsidy:
				if (!CheckSubsidyDuplicate(s)) {
					s->age = 0;
					pair = SetupSubsidyDecodeParam(s, 0);
					AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
					modified = true;
					break;
				}
			}
		} while (n--);
	}
no_add:;
	if (modified)
		InvalidateWindow(WC_SUBSIDIES_LIST, 0);
}

static const SaveLoad _subsidies_desc[] = {
	SLE_VAR(Subsidy,cargo_type,		SLE_UINT8),
	SLE_VAR(Subsidy,age,					SLE_UINT8),
	SLE_CONDVAR(Subsidy,from,			SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
	SLE_CONDVAR(Subsidy,from,			SLE_UINT16, 5, 255),
	SLE_CONDVAR(Subsidy,to,				SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
	SLE_CONDVAR(Subsidy,to,				SLE_UINT16, 5, 255),
	SLE_END()
};

static void Save_SUBS(void)
{
	int i;
	Subsidy *s;

	for(i=0; i!=lengthof(_subsidies); i++) {
		s = &_subsidies[i];
		if (s->cargo_type != 0xFF) {
			SlSetArrayIndex(i);
			SlObject(s, _subsidies_desc);
		}
	}
}

static void Load_SUBS(void)
{
	int index;
	while ((index = SlIterateArray()) != -1)
		SlObject(&_subsidies[index], _subsidies_desc);
}

int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, byte cargo_type)
{
	int cargo = cargo_type;
	byte f;

	/* zero the distance if it's the bank and very short transport. */
	if (_opt.landscape == LT_NORMAL && cargo == CT_VALUABLES && dist < 10)
		dist = 0;

	f = 255;
	if (transit_days > _cargoc.transit_days_1[cargo]) {
		transit_days -= _cargoc.transit_days_1[cargo];
		f -= transit_days;

		if (transit_days > _cargoc.transit_days_2[cargo]) {
			transit_days -= _cargoc.transit_days_2[cargo];

			if (f < transit_days)
				f = 0;
			else
				f -= transit_days;
		}
	}
	if (f < 31) f = 31;

	return BIGMULSS(dist * f * num_pieces, _cargo_payment_rates[cargo], 21);
}

static void DeliverGoodsToIndustry(TileIndex xy, byte cargo_type, int num_pieces)
{
	Industry *ind, *best;
	int t, u;

	/* Check if there's an industry close to the station that accepts
	 * the cargo */
	best = NULL;
	u = _patches.station_spread + 8;
	FOR_ALL_INDUSTRIES(ind) {
		if (ind->xy != 0 && (cargo_type == ind->accepts_cargo[0] || cargo_type
				 == ind->accepts_cargo[1] || cargo_type == ind->accepts_cargo[2]) &&
				 ind->produced_cargo[0] != 0xFF &&
				 ind->produced_cargo[0] != cargo_type &&
				 (t = DistanceManhattan(ind->xy, xy)) < 2 * u) {
			u = t;
			best = ind;
		}
	}

	/* Found one? */
	if (best != NULL) {
		best->was_cargo_delivered = true;
		best->cargo_waiting[0] = min(best->cargo_waiting[0] + num_pieces, 0xFFFF);
	}
}

static bool CheckSubsidised(Station *from, Station *to, byte cargo_type)
{
	Subsidy *s;
	TileIndex xy;
	Pair pair;
	Player *p;

	// check if there is an already existing subsidy that applies to us
	for(s=_subsidies; s != endof(_subsidies); s++) {
		if (s->cargo_type == cargo_type &&
				s->age >= 12 &&
				s->from == from->index &&
				s->to == to->index)
			return true;
	}

	/* check if there's a new subsidy that applies.. */
	for(s=_subsidies; s != endof(_subsidies); s++) {
		if (s->cargo_type == cargo_type && s->age < 12) {

			/* Check distance from source */
			if (cargo_type == CT_PASSENGERS || cargo_type == CT_MAIL) {
				xy = GetTown(s->from)->xy;
			} else {
				xy = (GetIndustry(s->from))->xy;
			}
			if (DistanceMax(xy, from->xy) > 9)
				continue;

			/* Check distance from dest */
			if (cargo_type == CT_PASSENGERS || cargo_type == CT_MAIL || cargo_type == CT_GOODS || cargo_type == CT_FOOD) {
				xy = GetTown(s->to)->xy;
			} else {
				xy = (GetIndustry(s->to))->xy;
			}

			if (DistanceMax(xy, to->xy) > 9)
				continue;

			/* Found a subsidy, change the values to indicate that it's in use */
			s->age = 12;
			s->from = from->index;
			s->to = to->index;

			/* Add a news item */
			pair = SetupSubsidyDecodeParam(s, 0);
			InjectDParam(2);

			p = DEREF_PLAYER(_current_player);
			SetDParam(0, p->name_1);
			SetDParam(1, p->name_2);
			AddNewsItem(
				STR_2031_SERVICE_SUBSIDY_AWARDED + _opt.diff.subsidy_multiplier,
				NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0),
				pair.a, pair.b);

			InvalidateWindow(WC_SUBSIDIES_LIST, 0);
			return true;
		}
	}
	return false;
}

static int32 DeliverGoods(int num_pieces, byte cargo_type, uint16 source, uint16 dest, byte days_in_transit)
{
	bool subsidised;
	Station *s_from, *s_to;
	int32 profit;

 	assert(num_pieces > 0);

	// Update player statistics
	{
		Player *p = DEREF_PLAYER(_current_player);
		p->cur_economy.delivered_cargo += num_pieces;
		SETBIT(p->cargo_types, cargo_type);
	}

	// Get station pointers.
	s_from = GetStation(source);
	s_to = GetStation(dest);

	// Check if a subsidy applies.
	subsidised = CheckSubsidised(s_from, s_to, cargo_type);

	// Increase town's counter for some special goods types
	{
		Town *t = s_to->town;
		if (cargo_type == CT_FOOD) t->new_act_food += num_pieces;
		if (cargo_type == CT_WATER)  t->new_act_water += num_pieces;
	}

	// Give the goods to the industry.
	DeliverGoodsToIndustry(s_to->xy, cargo_type, num_pieces);

	// Determine profit
	{
		int t = DistanceManhattan(s_from->xy, s_to->xy);
		int r = num_pieces;
		profit = 0;
		do {
			int u = min(r, 255);
			r -= u;
			profit += GetTransportedGoodsIncome(u, t, days_in_transit, cargo_type);
		} while (r != 0);
	}

	// Modify profit if a subsidy is in effect
	if (subsidised) {
		if (_opt.diff.subsidy_multiplier < 1) {
			/* 1.5x */
			profit += profit >> 1;
		} else if (_opt.diff.subsidy_multiplier == 1) {
			/* 2x */
			profit *= 2;
		} else if (_opt.diff.subsidy_multiplier == 2) {
			/* 3x */
			profit *= 3;
		} else {
			/* 4x */
			profit *= 4;
		}
	}

	// Computers get 25% extra profit if they're intelligent.
	if (_opt.diff.competitor_intelligence>=1 && !IS_HUMAN_PLAYER(_current_player))
		profit += profit >> 2;

	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)
 */
static bool LoadWait(const Vehicle *v, const Vehicle *u) {
	const Vehicle *w;
	const Vehicle *x;
	bool has_any_cargo = false;

	if (!(u->current_order.flags & OF_FULL_LOAD)) return false;

	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;
			has_any_cargo = true;
		}
	}

	FOR_ALL_VEHICLES(x) {
		if ((x->type != VEH_Train || x->subtype == TS_Front_Engine) && // for all locs
				u->last_station_visited == x->last_station_visited && // at the same station
				!(x->vehstatus & VS_STOPPED) && // not stopped
				x->current_order.type == OT_LOADING && // loading
				u != x) { // not itself
			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;

				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;
			}
		}
	}

	return false;
}

int LoadUnloadVehicle(Vehicle *v)
{
	int profit = 0;
	int unloading_time = 20;
	Vehicle *u = v;
	int result = 0;
	uint16 last_visited;
	Station *st;
	GoodsEntry *ge;
	int t;
	uint count, cap;
	byte old_player;
	bool completely_empty = true;

	assert(v->current_order.type == OT_LOADING);

	v->cur_speed = 0;

	old_player = _current_player;
	_current_player = v->owner;

	st = GetStation(last_visited = v->last_station_visited);

	for (; v != NULL; v = v->next) {
		if (v->cargo_cap == 0) continue;

		ge = &st->goods[v->cargo_type];

		/* unload? */
		if (v->cargo_count != 0) {
			if (v->cargo_source != last_visited && ge->waiting_acceptance & 0x8000) {
				// deliver goods to the station
				st->time_since_unload = 0;

				unloading_time += v->cargo_count; /* TTDBUG: bug in original TTD */
				profit += DeliverGoods(v->cargo_count, v->cargo_type, v->cargo_source, last_visited, v->cargo_days);
				result |= 1;
				v->cargo_count = 0;
			} else if (u->current_order.flags & OF_UNLOAD) {
				/* unload goods and let it wait at the station */
				st->time_since_unload = 0;

				if ((t=ge->waiting_acceptance & 0xFFF) == 0) {
					// No goods waiting at station
					ge->enroute_time = v->cargo_days;
					ge->enroute_from = v->cargo_source;
				} 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;
				}
				// Update amount of waiting cargo
				ge->waiting_acceptance = (ge->waiting_acceptance &~0xFFF) | min(v->cargo_count + t, 0xFFF);
				result |= 2;
				v->cargo_count = 0;
			}

			if (v->cargo_count != 0)
				completely_empty = false;
		}

		/* don't pick up goods that we unloaded */
		if (u->current_order.flags & OF_UNLOAD) continue;

		/* update stats */
		ge->days_since_pickup = 0;
		t = u->max_speed;
		if (u->type == VEH_Road) t >>=1;

		// if last speed is 0, we treat that as if no vehicle has ever visited the station.
		ge->last_speed = t < 255 ? t : 255;
		ge->last_age = _cur_year - v->build_year;

		// If there's goods waiting at the station, and the vehicle
		//  has capacity for it, load it on the vehicle.
		if ((count=ge->waiting_acceptance & 0xFFF) != 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 && LoadWait(v,u)) continue;

			/* TODO: Regarding this, when we do gradual loading, we
			 * should first unload all vehicles and then start
			 * loading them. Since this will cause
			 * VEHICLE_TRIGGER_EMPTY to be called at the time when
			 * the whole vehicle chain is really totally empty, the
			 * @completely_empty assignment can then be safely
			 * removed; that's how TTDPatch behaves too. --pasky */
			completely_empty = false;

			if (cap > count) cap = count;
			v->cargo_count += cap;
			ge->waiting_acceptance -= cap;
			unloading_time += cap;
			st->time_since_load = 0;

			// And record the source of the cargo, and the days in travel.
			v->cargo_source = ge->enroute_from;
			v->cargo_days = ge->enroute_time;
			result |= 2;
			st->last_vehicle = v->index;
		}
	}

	v = u;

	if (v->type == VEH_Train) {
		int num = - (int)GetStationPlatforms(st, v->tile) * 2;
		do num++; while ( (v=v->next) != NULL);
		if (num > 0) {
			unloading_time <<=1;
			unloading_time += num * unloading_time;
		}
		v = u;
	}

	v->load_unload_time_rem = unloading_time;

	if (completely_empty) {
		TriggerVehicle(v, VEHICLE_TRIGGER_EMPTY);
	}

	if (result != 0) {
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);

		if (result & 2)
			InvalidateWindow(WC_STATION_VIEW, last_visited);

		if (profit != 0) {
			v->profit_this_year += profit;
			SubtractMoneyFromPlayer(-profit);

			if (_current_player == _local_player)
				SndPlayVehicleFx(SND_14_CASHTILL, v);

			ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, -profit);
		}
	}

	_current_player = old_player;
	return result;
}

void PlayersMonthlyLoop(void)
{
	PlayersGenStatistics();
	if (_patches.inflation && _cur_year < MAX_YEAR_END)
		AddInflation();
	PlayersPayInterest();
	// Reset the _current_player flag
	_current_player = OWNER_NONE;
	HandleEconomyFluctuations();
	SubsidyMonthlyHandler();
}

static void DoAcquireCompany(Player *p)
{
	Player *owner;
	int i,pi;
	int64 value;

	SetDParam(0, p->name_1);
	SetDParam(1, p->name_2);
	SetDParam(2, p->bankrupt_value);
	AddNewsItem( (StringID)(_current_player + 16*2), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);

	// original code does this a little bit differently
	pi = p->index;
	ChangeOwnershipOfPlayerItems(pi, _current_player);

	if (p->bankrupt_value == 0) {
		owner = DEREF_PLAYER(_current_player);
		owner->current_loan += p->current_loan;
	}

	value = CalculateCompanyValue(p) >> 2;
	for(i=0; i!=4; i++) {
		if (p->share_owners[i] != 0xFF) {
			owner = DEREF_PLAYER(p->share_owners[i]);
			owner->money64 += value;
			owner->yearly_expenses[0][EXPENSES_OTHER] += value;
			UpdatePlayerMoney32(owner);
		}
	}

	p->is_active = false;

	DeletePlayerWindows(pi);
	RebuildVehicleLists();	//Updates the open windows to add the newly acquired vehicles to the lists
}

extern int GetAmountOwnedBy(Player *p, byte owner);

/** Acquire shares in an opposing company.
 * @param x,y unused
 * @param p1 player to buy the shares from
 * @param p2 unused
 */
int32 CmdBuyShareInCompany(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	Player *p;
	int64 cost;

	/* Check if buying shares is allowed (protection against modified clients */
	if (p1 >= MAX_PLAYERS || !_patches.allow_shares) return CMD_ERROR;

	SET_EXPENSES_TYPE(EXPENSES_OTHER);
	p = DEREF_PLAYER(p1);

	/* Protect new companies from hostile takeovers */
	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, OWNER_SPECTATOR) == 0) return 0;

	/* We can not buy out a real player (temporarily). TODO: well, enable it obviously */
	if (GetAmountOwnedBy(p, OWNER_SPECTATOR) == 1 && !p->is_ai) return 0;

	cost = CalculateCompanyValue(p) >> 2;
	if (flags & DC_EXEC) {
		int i;
		byte *b = p->share_owners;

		while (*b != 0xFF) b++; /* share owners is guaranteed to contain at least one 0xFF */
		*b = _current_player;

		for (i = 0; p->share_owners[i] == _current_player;) {
			if (++i == 4) {
				p->bankrupt_value = 0;
				DoAcquireCompany(p);
				break;
			}
		}
		InvalidateWindow(WC_COMPANY, (int)p1);
	}
	return cost;
}

/** Sell shares in an opposing company.
 * @param x,y unused
 * @param p1 player to sell the shares from
 * @param p2 unused
 */
int32 CmdSellShareInCompany(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	Player *p;
	int64 cost;

	/* Check if buying shares is allowed (protection against modified clients */
	if (p1 >= MAX_PLAYERS || !_patches.allow_shares) return CMD_ERROR;

	SET_EXPENSES_TYPE(EXPENSES_OTHER);
	p = DEREF_PLAYER(p1);

	/* Those lines are here for network-protection (clients can be slow) */
	if (GetAmountOwnedBy(p, _current_player) == 0) return 0;

	/* adjust it a little to make it less profitable to sell and buy */
	cost = CalculateCompanyValue(p) >> 2;
	cost = -(cost - (cost >> 7));

	if (flags & DC_EXEC) {
		byte *b = p->share_owners;
		while (*b != _current_player) b++; /* share owners is guaranteed to contain player */
		*b = 0xFF;
		InvalidateWindow(WC_COMPANY, (int)p1);
	}
	return cost;
}

/** Buy up another company.
 * When a competing company is gone bankrupt you get the chance to purchase
 * that company.
 * @todo currently this only works for AI players
 * @param x,y unused
 * @param p1 player/company to buy up
 * @param p2 unused
 */
int32 CmdBuyCompany(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	Player *p;

	/* Disable takeovers in multiplayer games */
	if (p1 >= MAX_PLAYERS || _networking) return CMD_ERROR;

	SET_EXPENSES_TYPE(EXPENSES_OTHER);
	p = DEREF_PLAYER(p1);

	if (!p->is_ai) return CMD_ERROR;

	if (flags & DC_EXEC) {
		DoAcquireCompany(p);
	}
	return p->bankrupt_value;
}

// Prices
static void SaveLoad_PRIC(void)
{
	SlArray(&_price, NUM_PRICES, SLE_INT32);
	SlArray(&_price_frac, NUM_PRICES, SLE_UINT16);
}

// Cargo payment rates
static void SaveLoad_CAPR(void)
{
	SlArray(&_cargo_payment_rates, NUM_CARGO, SLE_INT32);
	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()
};

// Economy variables
static void SaveLoad_ECMY(void)
{
	SlObject(&_economy, _economy_desc);
}

const ChunkHandler _economy_chunk_handlers[] = {
	{ 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH},
	{ 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH},
	{ 'SUBS', Save_SUBS,			Load_SUBS, CH_ARRAY},
	{ 'ECMY', SaveLoad_ECMY, SaveLoad_ECMY, CH_RIFF | CH_LAST},
};