Changeset - r7813:e50a8d29e5a0
[Not reviewed]
master
0 1 0
belugas - 17 years ago 2007-10-31 18:01:41
belugas@openttd.org
(svn r11363) -Codechange: Remove some magical numbers
1 file changed with 16 insertions and 6 deletions:
0 comments (0 inline, 0 general)
src/station_cmd.cpp
Show inline comments
 
@@ -133,192 +133,202 @@ static Station* GetStationAround(TileInd
 
/**
 
 * Function to check whether the given tile matches some criterion.
 
 * @param tile the tile to check
 
 * @return true if it matches, false otherwise
 
 */
 
typedef bool (*CMSAMatcher)(TileIndex tile);
 

	
 
/**
 
 * Counts the numbers of tiles matching a specific type in the area around
 
 * @param tile the center tile of the 'count area'
 
 * @param type the type of tile searched for
 
 * @param industry when type == MP_INDUSTRY, the type of the industry,
 
 *                 in all other cases this parameter is ignored
 
 * @return the result the noumber of matching tiles around
 
 */
 
static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
 
{
 
	int num = 0;
 

	
 
	for (int dx = -3; dx <= 3; dx++) {
 
		for (int dy = -3; dy <= 3; dy++) {
 
			if (cmp(TILE_MASK(tile + TileDiffXY(dx, dy)))) num++;
 
		}
 
	}
 

	
 
	return num;
 
}
 

	
 
/**
 
 * Check whether the tile is a mine.
 
 * @param tile the tile to investigate.
 
 * @return true if and only if the tile is a mine
 
 */
 
static bool CMSAMine(TileIndex tile)
 
{
 
	/* No industry */
 
	if (!IsTileType(tile, MP_INDUSTRY)) return false;
 

	
 
	const Industry *ind = GetIndustryByTile(tile);
 

	
 
	/* No extractive industry */
 
	if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_EXTRACTIVE) == 0) return false;
 

	
 
	for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
 
		/* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine */
 
		if (ind->produced_cargo[i] != CT_INVALID && (GetCargo(ind->produced_cargo[i])->classes & CC_LIQUID) == 0) return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Check whether the tile is water.
 
 * @param tile the tile to investigate.
 
 * @return true if and only if the tile is a mine
 
 */
 
static bool CMSAWater(TileIndex tile)
 
{
 
	return IsTileType(tile, MP_WATER) && IsWater(tile);
 
}
 

	
 
/**
 
 * Check whether the tile is a tree.
 
 * @param tile the tile to investigate.
 
 * @return true if and only if the tile is a mine
 
 */
 
static bool CMSATree(TileIndex tile)
 
{
 
	return IsTileType(tile, MP_TREES);
 
}
 

	
 
/**
 
 * Check whether the tile is a forest.
 
 * @param tile the tile to investigate.
 
 * @return true if and only if the tile is a mine
 
 */
 
static bool CMSAForest(TileIndex tile)
 
{
 
	/* No industry */
 
	if (!IsTileType(tile, MP_INDUSTRY)) return false;
 

	
 
	const Industry *ind = GetIndustryByTile(tile);
 

	
 
	/* No extractive industry */
 
	if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_ORGANIC) == 0) return false;
 

	
 
	for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
 
		/* The industry produces wood. */
 
		if (ind->produced_cargo[i] != CT_INVALID && GetCargo(ind->produced_cargo[i])->label == 'WOOD') return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
#define M(x) ((x) - STR_SV_STNAME)
 

	
 
enum StationNaming = {
 
	STATIONNAMING_RAIL = 0,
 
	STATIONNAMING_ROAD = 0,
 
	STATIONNAMING_AIRPORT,
 
	STATIONNAMING_OILRIG,
 
	STATIONNAMING_DOCK,
 
	STATIONNAMING_BUOY,
 
	STATIONNAMING_HELIPORT,
 
}
 

	
 
static bool GenerateStationName(Station *st, TileIndex tile, int flag)
 
{
 
	static const uint32 _gen_station_name_bits[] = {
 
		0,                                      /* 0 */
 
		1 << M(STR_SV_STNAME_AIRPORT),          /* 1 */
 
		1 << M(STR_SV_STNAME_OILFIELD),         /* 2 */
 
		1 << M(STR_SV_STNAME_DOCKS),            /* 3 */
 
		0x1FF << M(STR_SV_STNAME_BUOY_1),       /* 4 */
 
		1 << M(STR_SV_STNAME_HELIPORT),         /* 5 */
 
	};
 

	
 
	Town *t = st->town;
 
	uint32 free_names = (uint32)-1;
 
	int found;
 
	unsigned long tmp;
 

	
 
	{
 
		Station *s;
 

	
 
		FOR_ALL_STATIONS(s) {
 
			if (s != st && s->town == t) {
 
				uint str = M(s->string_id);
 
				if (str <= 0x20) {
 
					if (str == M(STR_SV_STNAME_FOREST))
 
						str = M(STR_SV_STNAME_WOODS);
 
					CLRBIT(free_names, str);
 
				}
 
			}
 
		}
 
	}
 

	
 
	/* check default names */
 
	tmp = free_names & _gen_station_name_bits[flag];
 
	if (tmp != 0) {
 
		found = FindFirstBit(tmp);
 
		goto done;
 
	}
 

	
 
	/* check mine? */
 
	if (HASBIT(free_names, M(STR_SV_STNAME_MINES))) {
 
		if (CountMapSquareAround(tile, CMSAMine) >= 2) {
 
			found = M(STR_SV_STNAME_MINES);
 
			goto done;
 
		}
 
	}
 

	
 
	/* check close enough to town to get central as name? */
 
	if (DistanceMax(tile, t->xy) < 8) {
 
		found = M(STR_SV_STNAME);
 
		if (HASBIT(free_names, M(STR_SV_STNAME))) goto done;
 

	
 
		found = M(STR_SV_STNAME_CENTRAL);
 
		if (HASBIT(free_names, M(STR_SV_STNAME_CENTRAL))) goto done;
 
	}
 

	
 
	/* Check lakeside */
 
	if (HASBIT(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
 
			DistanceFromEdge(tile) < 20 &&
 
			CountMapSquareAround(tile, CMSAWater) >= 5) {
 
		found = M(STR_SV_STNAME_LAKESIDE);
 
		goto done;
 
	}
 

	
 
	/* Check woods */
 
	if (HASBIT(free_names, M(STR_SV_STNAME_WOODS)) && (
 
				CountMapSquareAround(tile, CMSATree) >= 8 ||
 
				CountMapSquareAround(tile, CMSAForest) >= 2)
 
			) {
 
		found = _opt.landscape == LT_TROPIC ?
 
			M(STR_SV_STNAME_FOREST) : M(STR_SV_STNAME_WOODS);
 
		goto done;
 
	}
 

	
 
	/* check elevation compared to town */
 
	{
 
		uint z = GetTileZ(tile);
 
		uint z2 = GetTileZ(t->xy);
 
		if (z < z2) {
 
			found = M(STR_SV_STNAME_VALLEY);
 
			if (HASBIT(free_names, M(STR_SV_STNAME_VALLEY))) goto done;
 
		} else if (z > z2) {
 
			found = M(STR_SV_STNAME_HEIGHTS);
 
			if (HASBIT(free_names, M(STR_SV_STNAME_HEIGHTS))) goto done;
 
		}
 
	}
 

	
 
	/* check direction compared to town */
 
	{
 
		static const int8 _direction_and_table[] = {
 
			~( (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
 
			~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
 
			~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
 
			~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) ),
 
		};
 

	
 
		free_names &= _direction_and_table[
 
@@ -869,193 +879,193 @@ CommandCost CmdBuildRailroadStation(Tile
 
	CommandCost ret;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* Does the authority allow this? */
 
	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile_org)) return CMD_ERROR;
 
	if (!ValParamRailtype(p2 & 0xF)) return CMD_ERROR;
 

	
 
	/* unpack parameters */
 
	Axis axis = Extract<Axis, 0>(p1);
 
	uint numtracks = GB(p1,  8, 8);
 
	uint plat_len  = GB(p1, 16, 8);
 
	if (axis == AXIS_X) {
 
		w_org = plat_len;
 
		h_org = numtracks;
 
	} else {
 
		h_org = plat_len;
 
		w_org = numtracks;
 
	}
 

	
 
	if (h_org > _patches.station_spread || w_org > _patches.station_spread) return CMD_ERROR;
 

	
 
	/* these values are those that will be stored in train_tile and station_platforms */
 
	uint finalvalues[3];
 
	finalvalues[0] = tile_org;
 
	finalvalues[1] = w_org;
 
	finalvalues[2] = h_org;
 

	
 
	/* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
 
	StationID est = INVALID_STATION;
 
	/* If DC_EXEC is in flag, do not want to pass it to CheckFlatLandBelow, because of a nice bug
 
	 * for detail info, see:
 
	 * https://sourceforge.net/tracker/index.php?func=detail&aid=1029064&group_id=103924&atid=636365 */
 
	ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags & ~DC_EXEC, 5 << axis, _patches.nonuniform_stations ? &est : NULL);
 
	if (CmdFailed(ret)) return ret;
 
	CommandCost cost(ret.GetCost() + (numtracks * _price.train_station_track + _price.train_station_length) * plat_len);
 

	
 
	Station *st = NULL;
 
	bool check_surrounding = true;
 

	
 
	if (_patches.adjacent_stations) {
 
		if (est != INVALID_STATION) {
 
			if (HASBIT(p1, 24)) {
 
				/* You can't build an adjacent station over the top of one that
 
				 * already exists. */
 
				return_cmd_error(STR_MUST_REMOVE_RAILWAY_STATION_FIRST);
 
			} else {
 
				/* Extend the current station, and don't check whether it will
 
				 * be near any other stations. */
 
				st = GetStation(est);
 
				check_surrounding = false;
 
			}
 
		} else {
 
			/* There's no station here. Don't check the tiles surrounding this
 
			 * one if the player wanted to build an adjacent station. */
 
			if (HASBIT(p1, 24)) check_surrounding = false;
 
		}
 
	}
 

	
 
	if (check_surrounding) {
 
		/* Make sure there are no similar stations around us. */
 
		st = GetStationAround(tile_org, w_org, h_org, est);
 
		if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 
	}
 

	
 
	/* See if there is a deleted station close to us. */
 
	if (st == NULL) st = GetClosestStationFromTile(tile_org);
 

	
 
	/* In case of new station if DC_EXEC is NOT set we still need to create the station
 
	 * to test if everything is OK. In this case we need to delete it before return. */
 
	AutoPtrT<Station> st_auto_delete;
 

	
 
	if (st != NULL) {
 
		/* Reuse an existing station. */
 
		if (st->owner != _current_player)
 
			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
 

	
 
		if (st->train_tile != 0) {
 
			/* check if we want to expanding an already existing station? */
 
			if (_is_old_ai_player || !_patches.join_stations)
 
				return_cmd_error(STR_3005_TOO_CLOSE_TO_ANOTHER_RAILROAD);
 
			if (!CanExpandRailroadStation(st, finalvalues, axis))
 
				return CMD_ERROR;
 
		}
 

	
 
		/* XXX can't we pack this in the "else" part of the if above? */
 
		if (!st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TEST)) return CMD_ERROR;
 
	} else {
 
		/* allocate and initialize new station */
 
		st = new Station(tile_org);
 
		if (st == NULL) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
 

	
 
		/* ensure that in case of error (or no DC_EXEC) the station gets deleted upon return */
 
		st_auto_delete = st;
 

	
 
		st->town = ClosestTownFromTile(tile_org, (uint)-1);
 
		if (!GenerateStationName(st, tile_org, 0)) return CMD_ERROR;
 
		if (!GenerateStationName(st, tile_org, STATIONNAMING_RAIL)) return CMD_ERROR;
 

	
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC) != 0) {
 
			SETBIT(st->town->have_ratings, _current_player);
 
		}
 
	}
 

	
 
	/* Check if the given station class is valid */
 
	if (GB(p2, 8, 8) >= STAT_CLASS_MAX) return CMD_ERROR;
 

	
 
	/* Check if we can allocate a custom stationspec to this station */
 
	const StationSpec *statspec = GetCustomStationSpec((StationClassID)GB(p2, 8, 8), GB(p2, 16, 8));
 
	int specindex = AllocateSpecToStation(statspec, st, flags & DC_EXEC);
 
	if (specindex == -1) return CMD_ERROR;
 

	
 
	if (statspec != NULL) {
 
		/* Perform NewStation checks */
 

	
 
		/* Check if the station size is permitted */
 
		if (HASBIT(statspec->disallowed_platforms, numtracks - 1) || HASBIT(statspec->disallowed_lengths, plat_len - 1)) {
 
			return CMD_ERROR;
 
		}
 

	
 
		/* Check if the station is buildable */
 
		if (HASBIT(statspec->callbackmask, CBM_STATION_AVAIL) && GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE) == 0) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		TileIndexDiff tile_delta;
 
		byte *layout_ptr;
 
		byte numtracks_orig;
 
		Track track;
 

	
 
		/* Now really clear the land below the station
 
		 * It should never return CMD_ERROR.. but you never know ;)
 
		 * (a bit strange function name for it, but it really does clear the land, when DC_EXEC is in flags) */
 
		ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags, 5 << axis, _patches.nonuniform_stations ? &est : NULL);
 
		if (CmdFailed(ret)) return ret;
 

	
 
		st->train_tile = finalvalues[0];
 
		st->AddFacility(FACIL_TRAIN, finalvalues[0]);
 

	
 
		st->trainst_w = finalvalues[1];
 
		st->trainst_h = finalvalues[2];
 

	
 
		st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
 

	
 
		tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 
		track = AxisToTrack(axis);
 

	
 
		layout_ptr = (byte*)alloca(numtracks * plat_len);
 
		GetStationLayout(layout_ptr, numtracks, plat_len, statspec);
 

	
 
		numtracks_orig = numtracks;
 

	
 
		do {
 
			TileIndex tile = tile_org;
 
			int w = plat_len;
 
			do {
 
				byte layout = *layout_ptr++;
 
				MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, (RailType)GB(p2, 0, 4));
 
				SetCustomStationSpecIndex(tile, specindex);
 
				SetStationTileRandomBits(tile, GB(Random(), 0, 4));
 

	
 
				if (statspec != NULL) {
 
					/* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
 
					uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
 
					uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, st, tile);
 
					if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, (callback & ~1) + axis);
 
				}
 

	
 
				tile += tile_delta;
 
			} while (--w);
 
			SetSignalsOnBothDir(tile_org, track);
 
			YapfNotifyTrackLayoutChange(tile_org, track);
 
			tile_org += tile_delta ^ TileDiffXY(1, 1); // perpendicular to tile_delta
 
		} while (--numtracks);
 

	
 
		st->MarkTilesDirty(false);
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
		/* success, so don't delete the new station */
 
		st_auto_delete.Detach();
 
	}
 

	
 
	return cost;
 
}
 

	
 
static void MakeRailwayStationAreaSmaller(Station *st)
 
{
 
	uint w = st->trainst_w;
 
	uint h = st->trainst_h;
 
	TileIndex tile = st->train_tile;
 
@@ -1294,193 +1304,193 @@ static RoadStop **FindRoadStopSpot(bool 
 
 * @param tile tile to build the stop at
 
 * @param flags operation to perform
 
 * @param p1 entrance direction (DiagDirection)
 
 * @param p2 bit 0: 0 for Bus stops, 1 for truck stops
 
 *           bit 1: 0 for normal, 1 for drive-through
 
 *           bit 2..4: the roadtypes
 
 *           bit 5: allow stations directly adjacent to other stations.
 
 */
 
CommandCost CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	bool type = HASBIT(p2, 0);
 
	bool is_drive_through = HASBIT(p2, 1);
 
	bool build_over_road  = is_drive_through && IsTileType(tile, MP_ROAD) && GetRoadTileType(tile) == ROAD_TILE_NORMAL;
 
	bool town_owned_road  = build_over_road && IsTileOwner(tile, OWNER_TOWN);
 
	RoadTypes rts = (RoadTypes)GB(p2, 2, 3);
 

	
 
	if (rts == ROADTYPES_NONE || HASBIT(rts, ROADTYPE_HWAY)) return CMD_ERROR;
 

	
 
	/* Trams only have drive through stops */
 
	if (!is_drive_through && HASBIT(rts, ROADTYPE_TRAM)) return CMD_ERROR;
 

	
 
	/* Saveguard the parameters */
 
	if (!IsValidDiagDirection((DiagDirection)p1)) return CMD_ERROR;
 
	/* If it is a drive-through stop check for valid axis */
 
	if (is_drive_through && !IsValidAxis((Axis)p1)) return CMD_ERROR;
 
	/* Road bits in the wrong direction */
 
	if (build_over_road && (GetAllRoadBits(tile) & ((Axis)p1 == AXIS_X ? ROAD_Y : ROAD_X)) != 0) return_cmd_error(STR_DRIVE_THROUGH_ERROR_DIRECTION);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile)) return CMD_ERROR;
 

	
 
	CommandCost cost;
 

	
 
	/* Not allowed to build over this road */
 
	if (build_over_road) {
 
		if (IsTileOwner(tile, OWNER_TOWN) && !_patches.road_stop_on_town_road) return_cmd_error(STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD);
 
		if (GetRoadTileType(tile) != ROAD_TILE_NORMAL) return CMD_ERROR;
 

	
 
		/* Don't allow building the roadstop when vehicles are already driving on it */
 
		if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
 

	
 
		RoadTypes cur_rts = GetRoadTypes(tile);
 
		if (GetRoadOwner(tile, ROADTYPE_ROAD) != OWNER_TOWN && HASBIT(cur_rts, ROADTYPE_ROAD) && !CheckOwnership(GetRoadOwner(tile, ROADTYPE_ROAD))) return CMD_ERROR;
 
		if (HASBIT(cur_rts, ROADTYPE_TRAM) && !CheckOwnership(GetRoadOwner(tile, ROADTYPE_TRAM))) return CMD_ERROR;
 

	
 
		/* Do not remove roadtypes! */
 
		rts |= cur_rts;
 
	}
 
	cost = CheckFlatLandBelow(tile, 1, 1, flags, is_drive_through ? 5 << p1 : 1 << p1, NULL, !build_over_road);
 
	if (CmdFailed(cost)) return cost;
 

	
 
	Station *st = NULL;
 

	
 
	if (!_patches.adjacent_stations || !HASBIT(p2, 5)) {
 
		st = GetStationAround(tile, 1, 1, INVALID_STATION);
 
		if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 
	}
 

	
 
	/* Find a station close to us */
 
	if (st == NULL) st = GetClosestStationFromTile(tile);
 

	
 
	/* give us a road stop in the list, and check if something went wrong */
 
	RoadStop *road_stop = new RoadStop(tile);
 
	if (road_stop == NULL) {
 
		return_cmd_error(type ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS);
 
	}
 

	
 
	/* ensure that in case of error (or no DC_EXEC) the new road stop gets deleted upon return */
 
	AutoPtrT<RoadStop> rs_auto_delete(road_stop);
 

	
 
	if (st != NULL &&
 
			GetNumRoadStopsInStation(st, RoadStop::BUS) + GetNumRoadStopsInStation(st, RoadStop::TRUCK) >= RoadStop::LIMIT) {
 
		return_cmd_error(type ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS);
 
	}
 

	
 
	/* In case of new station if DC_EXEC is NOT set we still need to create the station
 
	 * to test if everything is OK. In this case we need to delete it before return. */
 
	AutoPtrT<Station> st_auto_delete;
 

	
 
	if (st != NULL) {
 
		if (st->owner != _current_player) {
 
			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
 
		}
 

	
 
		if (!st->rect.BeforeAddTile(tile, StationRect::ADD_TEST)) return CMD_ERROR;
 
	} else {
 
		/* allocate and initialize new station */
 
		st = new Station(tile);
 
		if (st == NULL) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
 

	
 
		/* ensure that in case of error (or no DC_EXEC) the new station gets deleted upon return */
 
		st_auto_delete = st;
 

	
 

	
 
		Town *t = st->town = ClosestTownFromTile(tile, (uint)-1);
 
		if (!GenerateStationName(st, tile, 0)) return CMD_ERROR;
 
		if (!GenerateStationName(st, tile, STATIONNAMING_ROAD)) return CMD_ERROR;
 

	
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC) != 0) {
 
			SETBIT(t->have_ratings, _current_player);
 
		}
 

	
 
		st->sign.width_1 = 0;
 
	}
 

	
 
	cost.AddCost((type) ? _price.build_truck_station : _price.build_bus_station);
 

	
 
	if (flags & DC_EXEC) {
 
		/* Insert into linked list of RoadStops */
 
		RoadStop **currstop = FindRoadStopSpot(type, st);
 
		*currstop = road_stop;
 

	
 
		/*initialize an empty station */
 
		st->AddFacility((type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, tile);
 

	
 
		st->rect.BeforeAddTile(tile, StationRect::ADD_TRY);
 

	
 
		RoadStop::Type rs_type = type ? RoadStop::TRUCK : RoadStop::BUS;
 
		if (is_drive_through) {
 
			MakeDriveThroughRoadStop(tile, st->owner, st->index, rs_type, rts, (Axis)p1, town_owned_road);
 
		} else {
 
			MakeRoadStop(tile, st->owner, st->index, rs_type, rts, (DiagDirection)p1);
 
		}
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
		/* success, so don't delete the new station and the new road stop */
 
		st_auto_delete.Detach();
 
		rs_auto_delete.Detach();
 
	}
 
	return cost;
 
}
 

	
 
/** Remove a bus station
 
 * @param st Station to remove
 
 * @param flags operation to perform
 
 * @param tile TileIndex been queried
 
 * @return cost or failure of operation
 
 */
 
static CommandCost RemoveRoadStop(Station *st, uint32 flags, TileIndex tile)
 
{
 
	if (_current_player != OWNER_WATER && !CheckOwnership(st->owner)) {
 
		return CMD_ERROR;
 
	}
 

	
 
	bool is_truck = IsTruckStop(tile);
 

	
 
	RoadStop **primary_stop;
 
	RoadStop *cur_stop;
 
	if (is_truck) { // truck stop
 
		primary_stop = &st->truck_stops;
 
		cur_stop = GetRoadStopByTile(tile, RoadStop::TRUCK);
 
	} else {
 
		primary_stop = &st->bus_stops;
 
		cur_stop = GetRoadStopByTile(tile, RoadStop::BUS);
 
	}
 

	
 
	assert(cur_stop != NULL);
 

	
 
	if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		if (*primary_stop == cur_stop) {
 
			/* removed the first stop in the list */
 
			*primary_stop = cur_stop->next;
 
			/* removed the only stop? */
 
			if (*primary_stop == NULL) {
 
				st->facilities &= (is_truck ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP);
 
			}
 
		} else {
 
			/* tell the predecessor in the list to skip this stop */
 
			RoadStop *pred = *primary_stop;
 
			while (pred->next != cur_stop) pred = pred->next;
 
			pred->next = cur_stop->next;
 
		}
 

	
 
		delete cur_stop;
 
		DoClearSquare(tile);
 
		st->rect.AfterRemoveTile(st, tile);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return CommandCost((is_truck) ? _price.remove_truck_station : _price.remove_bus_station);
 
}
 

	
 
/** Remove a bus or truck stop
 
 * @param tile tile to remove the stop from
 
 * @param flags operation to perform
 
 * @param p1 not used
 
@@ -1602,478 +1612,478 @@ static const byte * const _airport_secti
 
	_airport_sections_international,     // International Airport (xlarge)
 
	_airport_sections_commuter,          // Commuter Airport (small)
 
	_airport_sections_helidepot,         // Helidepot
 
	_airport_sections_intercontinental,  // Intercontinental Airport (xxlarge)
 
	_airport_sections_helistation        // Helistation
 
};
 

	
 
/** Place an Airport.
 
 * @param tile tile where airport will be built
 
 * @param flags operation to perform
 
 * @param p1 airport type, @see airport.h
 
 * @param p2 (bit 0) - allow airports directly adjacent to other airports.
 
 */
 
CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	bool airport_upgrade = true;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* Check if a valid, buildable airport was chosen for construction */
 
	if (p1 > lengthof(_airport_sections) || !HASBIT(GetValidAirports(), p1)) return CMD_ERROR;
 

	
 
	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
 
		return CMD_ERROR;
 

	
 
	Town *t = ClosestTownFromTile(tile, (uint)-1);
 

	
 
	/* Check if local auth refuses a new airport */
 
	{
 
		uint num = 0;
 
		const Station *st;
 
		FOR_ALL_STATIONS(st) {
 
			if (st->town == t && st->facilities&FACIL_AIRPORT && st->airport_type != AT_OILRIG)
 
				num++;
 
		}
 
		if (num >= 2) {
 
			SetDParam(0, t->index);
 
			return_cmd_error(STR_2035_LOCAL_AUTHORITY_REFUSES);
 
		}
 
	}
 

	
 
	const AirportFTAClass *afc = GetAirport(p1);
 
	int w = afc->size_x;
 
	int h = afc->size_y;
 

	
 
	CommandCost ret = CheckFlatLandBelow(tile, w, h, flags, 0, NULL);
 
	if (CmdFailed(ret)) return ret;
 
	CommandCost cost(ret.GetCost());
 

	
 
	Station *st = NULL;
 

	
 
	if (!_patches.adjacent_stations || !HASBIT(p2, 0)) {
 
		st = GetStationAround(tile, w, h, INVALID_STATION);
 
		if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 
	}
 

	
 
	/* Find a station close to us */
 
	if (st == NULL) st = GetClosestStationFromTile(tile);
 

	
 
	if (w > _patches.station_spread || h > _patches.station_spread) {
 
		_error_message = STR_306C_STATION_TOO_SPREAD_OUT;
 
		return CMD_ERROR;
 
	}
 

	
 
	/* In case of new station if DC_EXEC is NOT set we still need to create the station
 
	 * to test if everything is OK. In this case we need to delete it before return. */
 
	AutoPtrT<Station> st_auto_delete;
 

	
 
	if (st != NULL) {
 
		if (st->owner != _current_player)
 
			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
 

	
 
		if (!st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TEST)) return CMD_ERROR;
 

	
 
		if (st->airport_tile != 0)
 
			return_cmd_error(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT);
 
	} else {
 
		airport_upgrade = false;
 

	
 
		/* allocate and initialize new station */
 
		st = new Station(tile);
 
		if (st == NULL) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
 

	
 
		/* ensure that in case of error (or no DC_EXEC) the station gets deleted upon return */
 
		st_auto_delete = st;
 

	
 
		st->town = t;
 

	
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC) != 0) {
 
			SETBIT(t->have_ratings, _current_player);
 
		}
 

	
 
		st->sign.width_1 = 0;
 

	
 
		/* If only helicopters may use the airport generate a helicopter related (5)
 
		 * station name, otherwise generate a normal airport name (1) */
 
		if (!GenerateStationName(st, tile, !(afc->flags & AirportFTAClass::AIRPLANES) ? 5 : 1)) {
 
		if (!GenerateStationName(st, tile, !(afc->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_HELIPORT : STATIONNAMING_AIRPORT)) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	cost.AddCost(_price.build_airport * w * h);
 

	
 
	if (flags & DC_EXEC) {
 
		st->airport_tile = tile;
 
		st->AddFacility(FACIL_AIRPORT, tile);
 
		st->airport_type = (byte)p1;
 
		st->airport_flags = 0;
 

	
 
		st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
 

	
 
		/* if airport was demolished while planes were en-route to it, the
 
		 * positions can no longer be the same (v->u.air.pos), since different
 
		 * airports have different indexes. So update all planes en-route to this
 
		 * airport. Only update if
 
		 * 1. airport is upgraded
 
		 * 2. airport is added to existing station (unfortunately unavoideable)
 
		 */
 
		if (airport_upgrade) UpdateAirplanesOnNewStation(st);
 

	
 
		{
 
			const byte *b = _airport_sections[p1];
 

	
 
			BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
 
				MakeAirport(tile_cur, st->owner, st->index, *b - ((*b < 67) ? 8 : 24));
 
				b++;
 
			} END_TILE_LOOP(tile_cur, w, h, tile)
 
		}
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
		/* success, so don't delete the new station */
 
		st_auto_delete.Detach();
 
	}
 

	
 
	return cost;
 
}
 

	
 
static CommandCost RemoveAirport(Station *st, uint32 flags)
 
{
 
	if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
 
		return CMD_ERROR;
 

	
 
	TileIndex tile = st->airport_tile;
 

	
 
	const AirportFTAClass *afc = st->Airport();
 
	int w = afc->size_x;
 
	int h = afc->size_y;
 

	
 
	CommandCost cost(w * h * _price.remove_airport);
 

	
 
	Vehicle *v;
 
	FOR_ALL_VEHICLES(v) {
 
		if (!(v->type == VEH_AIRCRAFT && IsNormalAircraft(v))) continue;
 

	
 
		if (v->u.air.targetairport == st->index && v->u.air.state != FLYING) return CMD_ERROR;
 
	}
 

	
 
	BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
 
		if (!EnsureNoVehicleOnGround(tile_cur)) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			DeleteAnimatedTile(tile_cur);
 
			DoClearSquare(tile_cur);
 
		}
 
	} END_TILE_LOOP(tile_cur, w, h, tile)
 

	
 
	if (flags & DC_EXEC) {
 
		for (uint i = 0; i < afc->nof_depots; ++i) {
 
			DeleteWindowById(
 
				WC_VEHICLE_DEPOT, tile + ToTileIndexDiff(afc->airport_depots[i])
 
			);
 
		}
 

	
 
		st->rect.AfterRemoveRect(st, tile, w, h);
 

	
 
		st->airport_tile = 0;
 
		st->facilities &= ~FACIL_AIRPORT;
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return cost;
 
}
 

	
 
/** Build a buoy.
 
 * @param tile tile where to place the bouy
 
 * @param flags operation to perform
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
CommandCost CmdBuildBuoy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!IsWaterTile(tile) || tile == 0) return_cmd_error(STR_304B_SITE_UNSUITABLE);
 
	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
	/* allocate and initialize new station */
 
	Station *st = new Station(tile);
 
	if (st == NULL) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
 

	
 
	/* ensure that in case of error (or no DC_EXEC) the station gets deleted upon return */
 
	AutoPtrT<Station> st_auto_delete(st);
 

	
 
	st->town = ClosestTownFromTile(tile, (uint)-1);
 
	st->sign.width_1 = 0;
 

	
 
	if (!GenerateStationName(st, tile, 4)) return CMD_ERROR;
 
	if (!GenerateStationName(st, tile, STATIONNAMING_BUOY)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		st->dock_tile = tile;
 
		st->facilities |= FACIL_DOCK;
 
		/* Buoys are marked in the Station struct by this flag. Yes, it is this
 
		 * braindead.. */
 
		st->had_vehicle_of_type |= HVOT_BUOY;
 
		st->owner = OWNER_NONE;
 

	
 
		st->build_date = _date;
 

	
 
		MakeBuoy(tile, st->index);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
		/* success, so don't delete the new station */
 
		st_auto_delete.Detach();
 
	}
 

	
 
	return CommandCost(_price.build_dock);
 
}
 

	
 
/* Checks if any ship is servicing the buoy specified. Returns yes or no */
 
static bool CheckShipsOnBuoy(Station *st)
 
{
 
	const Vehicle *v;
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_SHIP) {
 
			const Order *order;
 
			FOR_VEHICLE_ORDERS(v, order) {
 
				if (order->type == OT_GOTO_STATION && order->dest == st->index) {
 
					return true;
 
				}
 
			}
 
		}
 
	}
 
	return false;
 
}
 

	
 
static CommandCost RemoveBuoy(Station *st, uint32 flags)
 
{
 
	/* XXX: strange stuff */
 
	if (!IsValidPlayer(_current_player))  return_cmd_error(INVALID_STRING_ID);
 

	
 
	TileIndex tile = st->dock_tile;
 

	
 
	if (CheckShipsOnBuoy(st))   return_cmd_error(STR_BUOY_IS_IN_USE);
 
	if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		st->dock_tile = 0;
 
		/* Buoys are marked in the Station struct by this flag. Yes, it is this
 
		 * braindead.. */
 
		st->facilities &= ~FACIL_DOCK;
 
		st->had_vehicle_of_type &= ~HVOT_BUOY;
 

	
 
		/* We have to set the water tile's state to the same state as before the
 
		 * buoy was placed. Otherwise one could plant a buoy on a canal edge,
 
		 * remove it and flood the land (if the canal edge is at level 0) */
 
		Owner o = GetTileOwner(tile);
 
		if (o == OWNER_WATER) {
 
			MakeWater(tile);
 
		} else {
 
			MakeCanal(tile, o);
 
		}
 
		MarkTileDirtyByTile(tile);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return CommandCost(_price.remove_truck_station);
 
}
 

	
 
static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
 
	{-1,  0},
 
	{ 0,  0},
 
	{ 0,  0},
 
	{ 0, -1}
 
};
 
static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
 
static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
 

	
 
/** Build a dock/haven.
 
 * @param tile tile where dock will be built
 
 * @param flags operation to perform
 
 * @param p1 (bit 0) - allow docks directly adjacent to other docks.
 
 * @param p2 unused
 
 */
 
CommandCost CmdBuildDock(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	CommandCost cost;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	DiagDirection direction;
 
	switch (GetTileSlope(tile, NULL)) {
 
		case SLOPE_SW: direction = DIAGDIR_NE; break;
 
		case SLOPE_SE: direction = DIAGDIR_NW; break;
 
		case SLOPE_NW: direction = DIAGDIR_SE; break;
 
		case SLOPE_NE: direction = DIAGDIR_SW; break;
 
		default: return_cmd_error(STR_304B_SITE_UNSUITABLE);
 
	}
 

	
 
	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile)) return CMD_ERROR;
 

	
 
	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
	cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(cost)) return CMD_ERROR;
 

	
 
	TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
 

	
 
	if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
 
		return_cmd_error(STR_304B_SITE_UNSUITABLE);
 
	}
 

	
 
	if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
	cost = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(cost)) return CMD_ERROR;
 

	
 
	tile_cur += TileOffsByDiagDir(direction);
 
	if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
 
		return_cmd_error(STR_304B_SITE_UNSUITABLE);
 
	}
 

	
 
	/* middle */
 
	Station *st = NULL;
 

	
 
	if (!_patches.adjacent_stations || !HASBIT(p1, 0)) {
 
		st = GetStationAround(
 
				tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
 
				_dock_w_chk[direction], _dock_h_chk[direction], INVALID_STATION);
 
		if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 
	}
 

	
 
	/* Find a station close to us */
 
	if (st == NULL) st = GetClosestStationFromTile(tile);
 

	
 
	/* In case of new station if DC_EXEC is NOT set we still need to create the station
 
	 * to test if everything is OK. In this case we need to delete it before return. */
 
	AutoPtrT<Station> st_auto_delete;
 

	
 
	if (st != NULL) {
 
		if (st->owner != _current_player)
 
			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
 

	
 
		if (!st->rect.BeforeAddRect(tile, _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TEST)) return CMD_ERROR;
 

	
 
		if (st->dock_tile != 0) return_cmd_error(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK);
 
	} else {
 
		/* allocate and initialize new station */
 
		st = new Station(tile);
 
		if (st == NULL) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
 

	
 
		/* ensure that in case of error (or no DC_EXEC) the station gets deleted upon return */
 
		st_auto_delete = st;
 

	
 
		Town *t = st->town = ClosestTownFromTile(tile, (uint)-1);
 

	
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC) != 0) {
 
			SETBIT(t->have_ratings, _current_player);
 
		}
 

	
 
		st->sign.width_1 = 0;
 

	
 
		if (!GenerateStationName(st, tile, 3)) return CMD_ERROR;
 
		if (!GenerateStationName(st, tile, STATIONNAMING_DOCK)) return CMD_ERROR;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		st->dock_tile = tile;
 
		st->AddFacility(FACIL_DOCK, tile);
 

	
 
		st->rect.BeforeAddRect(tile, _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TRY);
 

	
 
		MakeDock(tile, st->owner, st->index, direction);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
		/* success, so don't delete the new station */
 
		st_auto_delete.Detach();
 
	}
 
	return CommandCost(_price.build_dock);
 
}
 

	
 
static CommandCost RemoveDock(Station *st, uint32 flags)
 
{
 
	if (!CheckOwnership(st->owner)) return CMD_ERROR;
 

	
 
	TileIndex tile1 = st->dock_tile;
 
	TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
 

	
 
	if (!EnsureNoVehicleOnGround(tile1)) return CMD_ERROR;
 
	if (!EnsureNoVehicleOnGround(tile2)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		DoClearSquare(tile1);
 
		MakeWater(tile2);
 

	
 
		st->rect.AfterRemoveTile(st, tile1);
 
		st->rect.AfterRemoveTile(st, tile2);
 

	
 
		MarkTileDirtyByTile(tile2);
 

	
 
		st->dock_tile = 0;
 
		st->facilities &= ~FACIL_DOCK;
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return CommandCost(_price.remove_dock);
 
}
 

	
 
#include "table/station_land.h"
 

	
 
const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
 
{
 
	return &_station_display_datas[st][gfx];
 
}
 

	
 
/* For drawing canal edges on buoys */
 
extern void DrawCanalWater(TileIndex tile);
 

	
 
static void DrawTile_Station(TileInfo *ti)
 
{
 
	const DrawTileSprites *t = NULL;
 
	RailType railtype;
 
	RoadTypes roadtypes;
 
	if (IsRailwayStation(ti->tile)) {
 
		railtype = GetRailType(ti->tile);
 
		roadtypes = ROADTYPES_NONE;
 
	} else {
 
		roadtypes = GetRoadTypes(ti->tile);
 
		railtype = RAILTYPE_BEGIN;
 
	}
 
	const RailtypeInfo *rti = GetRailTypeInfo(railtype);
 
	uint32 relocation = 0;
 
	const Station *st = NULL;
 
	const StationSpec *statspec = NULL;
 
	PlayerID owner = GetTileOwner(ti->tile);
 

	
 
	SpriteID palette;
 
	if (IsValidPlayer(owner)) {
 
		palette = PLAYER_SPRITE_COLOR(owner);
 
	} else {
 
		/* Some stations are not owner by a player, namely oil rigs */
 
		palette = PALETTE_TO_GREY;
 
	}
 

	
 
	/* don't show foundation for docks */
 
	if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile))
 
		DrawFoundation(ti, FOUNDATION_LEVELED);
 

	
 
	if (IsCustomStationSpecIndex(ti->tile)) {
 
		/* look for customization */
 
		st = GetStationByTile(ti->tile);
 
		statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
 

	
 
		//debug("Cust-o-mized %p", statspec);
 

	
 
@@ -2656,193 +2666,193 @@ uint MoveGoodsToStation(TileIndex tile, 
 
						const int x_min_prod = max_rad + 1;
 
						const int x_max_prod = max_rad + w_prod;
 
						const int y_min_prod = max_rad + 1;
 
						const int y_max_prod = max_rad + h_prod;
 

	
 
						int rad = FindCatchmentRadius(st);
 

	
 
						int x_dist = min(w_cur - x_min_prod, x_max_prod - w_cur);
 
						if (w_cur < x_min_prod) {
 
							x_dist = x_min_prod - w_cur;
 
						} else if (w_cur > x_max_prod) {
 
							x_dist = w_cur - x_max_prod;
 
						}
 

	
 
						int y_dist = min(h_cur - y_min_prod, y_max_prod - h_cur);
 
						if (h_cur < y_min_prod) {
 
							y_dist = y_min_prod - h_cur;
 
						} else if (h_cur > y_max_prod) {
 
							y_dist = h_cur - y_max_prod;
 
						}
 

	
 
						if (x_dist > rad || y_dist > rad) break;
 
					}
 

	
 
					around[i] = st;
 
				}
 
				break;
 
			} else if (around[i] == st) {
 
				break;
 
			}
 
		}
 
	END_TILE_LOOP(cur_tile, w, h, tile - TileDiffXY(max_rad, max_rad))
 

	
 
	/* no stations around at all? */
 
	if (around[0] == NULL) return 0;
 

	
 
	if (around[1] == NULL) {
 
		/* only one station around */
 
		uint moved = (amount * around[0]->goods[type].rating >> 8) + 1;
 
		UpdateStationWaiting(around[0], type, moved);
 
		return moved;
 
	}
 

	
 
	/* several stations around, find the two with the highest rating */
 
	Station *st1 = NULL;
 
	Station *st2 = NULL;
 
	uint best_rating  = 0;
 
	uint best_rating2 = 0;
 

	
 
	for (uint i = 0; i != lengthof(around) && around[i] != NULL; i++) {
 
		if (around[i]->goods[type].rating >= best_rating) {
 
			best_rating2 = best_rating;
 
			st2 = st1;
 

	
 
			best_rating = around[i]->goods[type].rating;
 
			st1 = around[i];
 
		} else if (around[i]->goods[type].rating >= best_rating2) {
 
			best_rating2 = around[i]->goods[type].rating;
 
			st2 = around[i];
 
		}
 
	}
 

	
 
	assert(st1 != NULL);
 
	assert(st2 != NULL);
 
	assert(best_rating != 0 || best_rating2 != 0);
 

	
 
	/* the 2nd highest one gets a penalty */
 
	best_rating2 >>= 1;
 

	
 
	/* amount given to station 1 */
 
	uint t = (best_rating * (amount + 1)) / (best_rating + best_rating2);
 

	
 
	uint moved = 0;
 
	if (t != 0) {
 
		moved = t * best_rating / 256 + 1;
 
		amount -= t;
 
		UpdateStationWaiting(st1, type, moved);
 
	}
 

	
 
	if (amount != 0) {
 
		amount = amount * best_rating2 / 256 + 1;
 
		moved += amount;
 
		UpdateStationWaiting(st2, type, amount);
 
	}
 

	
 
	return moved;
 
}
 

	
 
void BuildOilRig(TileIndex tile)
 
{
 
	Station *st = new Station();
 

	
 
	if (st == NULL) {
 
		DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
 
		return;
 
	}
 
	if (!GenerateStationName(st, tile, 2)) {
 
	if (!GenerateStationName(st, tile, STATIONNAMING_OILRIG)) {
 
		DEBUG(misc, 0, "Can't allocate station-name for oilrig at 0x%X, reverting to oilrig only", tile);
 
		return;
 
	}
 

	
 
	st->town = ClosestTownFromTile(tile, (uint)-1);
 
	st->sign.width_1 = 0;
 

	
 
	MakeOilrig(tile, st->index);
 

	
 
	st->owner = OWNER_NONE;
 
	st->airport_flags = 0;
 
	st->airport_type = AT_OILRIG;
 
	st->xy = tile;
 
	st->bus_stops = NULL;
 
	st->truck_stops = NULL;
 
	st->airport_tile = tile;
 
	st->dock_tile = tile;
 
	st->train_tile = 0;
 
	st->had_vehicle_of_type = 0;
 
	st->time_since_load = 255;
 
	st->time_since_unload = 255;
 
	st->delete_ctr = 0;
 
	st->last_vehicle_type = VEH_INVALID;
 
	st->facilities = FACIL_AIRPORT | FACIL_DOCK;
 
	st->build_date = _date;
 

	
 
	for (CargoID j = 0; j < NUM_CARGO; j++) {
 
		st->goods[j].acceptance_pickup = 0;
 
		st->goods[j].days_since_pickup = 255;
 
		st->goods[j].rating = INITIAL_STATION_RATING;
 
		st->goods[j].last_speed = 0;
 
		st->goods[j].last_age = 255;
 
	}
 

	
 
	UpdateStationVirtCoordDirty(st);
 
	UpdateStationAcceptance(st, false);
 
}
 

	
 
void DeleteOilRig(TileIndex tile)
 
{
 
	Station* st = GetStationByTile(tile);
 

	
 
	MakeWater(tile);
 

	
 
	st->dock_tile = 0;
 
	st->airport_tile = 0;
 
	st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
 
	st->airport_flags = 0;
 
	UpdateStationVirtCoordDirty(st);
 
	if (st->facilities == 0) delete st;
 
}
 

	
 
static void ChangeTileOwner_Station(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	if (!IsTileOwner(tile, old_player)) return;
 

	
 
	if (new_player != PLAYER_SPECTATOR) {
 
		Station* st = GetStationByTile(tile);
 

	
 
		SetTileOwner(tile, new_player);
 
		st->owner = new_player;
 
		RebuildStationLists();
 
		InvalidateWindowClasses(WC_STATION_LIST);
 
	} else {
 
		if (IsDriveThroughStopTile(tile) && GetStopBuiltOnTownRoad(tile)) {
 
			/* For a drive-through stop on a town-owned road remove the stop and replace the road */
 
			DoCommand(tile, 0, (GetStationType(tile) == STATION_TRUCK) ? RoadStop::TRUCK : RoadStop::BUS, DC_EXEC, CMD_REMOVE_ROAD_STOP);
 
		} else {
 
			DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		}
 
	}
 
}
 

	
 
/**
 
 * Check if a drive-through road stop tile can be cleared.
 
 * Road stops built on town-owned roads check the conditions
 
 * that would allow clearing of the original road.
 
 * @param tile road stop tile to check
 
 * @return true if the road can be cleared
 
 */
 
static bool CanRemoveRoadWithStop(TileIndex tile)
 
{
 
	/* The road can always be cleared if it was not a town-owned road */
 
	if (!GetStopBuiltOnTownRoad(tile)) return true;
 

	
 
	bool edge_road;
 
	return CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, &edge_road, ROADTYPE_ROAD) &&
 
				CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_TRAM), OWNER_TOWN, &edge_road, ROADTYPE_TRAM);
 
}
 

	
 
static CommandCost ClearTile_Station(TileIndex tile, byte flags)
 
{
 
	if (flags & DC_AUTO) {
 
		switch (GetStationType(tile)) {
 
			case STATION_RAIL:    return_cmd_error(STR_300B_MUST_DEMOLISH_RAILROAD);
 
			case STATION_AIRPORT: return_cmd_error(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST);
0 comments (0 inline, 0 general)