Esempio n. 1
0
/**
 * Check whether the given tile is suitable for a waypoint.
 * @param tile the tile to check for suitability
 * @param axis the axis of the waypoint
 * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error.
 */
static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint)
{
	/* if waypoint is set, then we have special handling to allow building on top of already existing waypoints.
	 * so waypoint points to INVALID_STATION if we can build on any waypoint.
	 * Or it points to a waypoint if we're only allowed to build on exactly that waypoint. */
	if (waypoint != NULL && IsTileType(tile, MP_STATION)) {
		if (!IsRailWaypoint(tile)) {
			return ClearTile_Station(tile, DC_AUTO); // get error message
		} else {
			StationID wp = GetStationIndex(tile);
			if (*waypoint == INVALID_STATION) {
				*waypoint = wp;
			} else if (*waypoint != wp) {
				return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING);
			}
		}
	}

	if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);

	Owner owner = GetTileOwner(tile);
	CommandCost ret = CheckOwnership(owner);
	if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile);
	if (ret.Failed()) return ret;

	Slope tileh = GetTileSlope(tile);
	if (tileh != SLOPE_FLAT &&
			(!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) {
		return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
	}

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

	return CommandCost();
}
Esempio n. 2
0
/**
 * Applies a foundation to a slope.
 *
 * @pre      Foundation and slope must be valid combined.
 * @param f  The #Foundation.
 * @param s  The #Slope to modify.
 * @return   Increment to the tile Z coordinate.
 */
uint ApplyFoundationToSlope(Foundation f, Slope *s)
{
	if (!IsFoundation(f)) return 0;

	if (IsLeveledFoundation(f)) {
		uint dz = TILE_HEIGHT + (IsSteepSlope(*s) ? TILE_HEIGHT : 0);
		*s = SLOPE_FLAT;
		return dz;
	}

	if (f != FOUNDATION_STEEP_BOTH && IsNonContinuousFoundation(f)) {
		*s = HalftileSlope(*s, GetHalftileFoundationCorner(f));
		return 0;
	}

	if (IsSpecialRailFoundation(f)) {
		*s = SlopeWithThreeCornersRaised(OppositeCorner(GetRailFoundationCorner(f)));
		return 0;
	}

	uint dz = IsSteepSlope(*s) ? TILE_HEIGHT : 0;
	Corner highest_corner = GetHighestSlopeCorner(*s);

	switch (f) {
		case FOUNDATION_INCLINED_X:
			*s = (((highest_corner == CORNER_W) || (highest_corner == CORNER_S)) ? SLOPE_SW : SLOPE_NE);
			break;

		case FOUNDATION_INCLINED_Y:
			*s = (((highest_corner == CORNER_S) || (highest_corner == CORNER_E)) ? SLOPE_SE : SLOPE_NW);
			break;

		case FOUNDATION_STEEP_LOWER:
			*s = SlopeWithOneCornerRaised(highest_corner);
			break;

		case FOUNDATION_STEEP_BOTH:
			*s = HalftileSlope(SlopeWithOneCornerRaised(highest_corner), highest_corner);
			break;

		default: NOT_REACHED();
	}
	return dz;
}
Esempio n. 3
0
/**
 * Check whether the given tile is suitable for a waypoint.
 * @param tile the tile to check for suitability
 * @param axis the axis of the waypoint
 * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error.
 * @param flags flags for the command
 */
static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint, DoCommandFlag flags)
{
	/* if waypoint is set, then we have special handling to allow building on top of already existing waypoints.
	 * so waypoint points to INVALID_STATION if we can build on any waypoint.
	 * Or it points to a waypoint if we're only allowed to build on exactly that waypoint. */
	if (waypoint != NULL && IsTileType(tile, MP_STATION)) {
		if (!IsRailWaypoint(tile)) {
			return ClearTile_Station(tile, DC_AUTO); // get error message
		} else {
			StationID wp = GetStationIndex(tile);
			if (*waypoint == INVALID_STATION) {
				*waypoint = wp;
			} else if (*waypoint != wp) {
				return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING);
			}
		}
	}

	/* When pasting a waypoint, there may be no track yet (it will be placed when DC_EXEC'ing).
	 * Ignore that so we can calculate the cost more precisely. */
	bool ignore_lack_of_tracks = (flags & DC_PASTE) && !(flags & DC_EXEC);

	if (!ignore_lack_of_tracks || IsTileType(tile, MP_RAILWAY)) {
		if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
	}

	Owner owner = GetTileOwner(tile);
	if (!ignore_lack_of_tracks || owner != OWNER_NONE) {
		CommandCost ret = CheckOwnership(owner);
		if (ret.Failed()) return ret;
	}

	CommandCost ret = EnsureNoVehicleOnGround(tile);
	if (ret.Failed()) return ret;

	Slope tileh = GetTileSlope(tile);
	if (tileh != SLOPE_FLAT &&
			(!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) {
		return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
	}

	if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);

	return CommandCost();
}
Esempio n. 4
0
/**
 * Lookup function for building road parts when building on slopes is enabled.
 * @param slope The slope of the tile to examine.
 * @param existing The existing neighbours.
 * @param start The part that should be build first.
 * @param end The part that will be build second.
 * @return 0 when the build parts do not connect, 1 when they do connect once
 *         they are build or 2 when building the first part automatically
 *         builds the second part.
 */
static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end)
{
	/* Steep slopes behave the same as slopes with one corner raised. */
	if (IsSteepSlope(slope)) {
		slope = SlopeWithOneCornerRaised(GetHighestSlopeCorner(slope));
	}

	/* The slope is not steep. Furthermore lots of slopes are generally the
	 * same but are only rotated. So to reduce the amount of lookup work that
	 * needs to be done the data is made uniform. This means rotating the
	 * existing parts and updating the slope. */
	static const ::Slope base_slopes[] = {
		SLOPE_FLAT, SLOPE_W,   SLOPE_W,   SLOPE_SW,
		SLOPE_W,    SLOPE_EW,  SLOPE_SW,  SLOPE_WSE,
		SLOPE_W,    SLOPE_SW,  SLOPE_EW,  SLOPE_WSE,
		SLOPE_SW,   SLOPE_WSE, SLOPE_WSE};
	static const byte base_rotates[] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 3, 2, 3, 2, 2, 1};

	if (slope >= (::Slope)lengthof(base_slopes)) {
		/* This slope is an invalid slope, so ignore it. */
		return -1;
	}
	byte base_rotate = base_rotates[slope];
	slope = base_slopes[slope];

	/* Some slopes don't need rotating, so return early when we know we do
	 * not need to rotate. */
	switch (slope) {
		case SLOPE_FLAT:
			/* Flat slopes can always be build. */
			return 1;

		case SLOPE_EW:
		case SLOPE_WSE:
			/* A slope similar to a SLOPE_EW or SLOPE_WSE will always cause
			 * foundations which makes them accessible from all sides. */
			return 1;

		case SLOPE_W:
		case SLOPE_SW:
			/* A slope for which we need perform some calculations. */
			break;

		default:
			/* An invalid slope. */
			return -1;
	}

	/* Now perform the actual rotation. */
	for (int j = 0; j < base_rotate; j++) {
		for (int i = 0; i < existing->size; i++) {
			existing->array[i] = RotateNeighbour(existing->array[i]);
		}
		start = RotateNeighbour(start);
		end   = RotateNeighbour(end);
	}

	/* Create roadbits out of the data for easier handling. */
	RoadBits start_roadbits    = NeighbourToRoadBits(start);
	RoadBits new_roadbits      = start_roadbits | NeighbourToRoadBits(end);
	RoadBits existing_roadbits = ROAD_NONE;
	for (int i = 0; i < existing->size; i++) {
		existing_roadbits |= NeighbourToRoadBits(existing->array[i]);
	}

	switch (slope) {
		case SLOPE_W:
			/* A slope similar to a SLOPE_W. */
			switch (new_roadbits) {
				case ROAD_N:
				case ROAD_E:
				case ROAD_S:
					/* Cannot build anything with a turn from the low side. */
					return 0;

				case ROAD_X:
				case ROAD_Y:
					/* A 'sloped' tile is going to be build. */
					if ((existing_roadbits | new_roadbits) != new_roadbits) {
						/* There is already a foundation on the tile, or at least
						 * another slope that is not compatible with the new one. */
						return 0;
					}
					/* If the start is in the low part, it is automatically
					 * building the second part too. */
					return ((start_roadbits & ROAD_E) && !(existing_roadbits & ROAD_W)) ? 2 : 1;

				default:
					/* Roadbits causing a foundation are going to be build.
					 * When the existing roadbits are slopes (the lower bits
					 * are used), this cannot be done. */
					if ((existing_roadbits | new_roadbits) == new_roadbits) return 1;
					return (existing_roadbits & ROAD_E) ? 0 : 1;
			}

		case SLOPE_SW:
			/* A slope similar to a SLOPE_SW. */
			switch (new_roadbits) {
				case ROAD_N:
				case ROAD_E:
					/* Cannot build anything with a turn from the low side. */
					return 0;

				case ROAD_X:
					/* A 'sloped' tile is going to be build. */
					if ((existing_roadbits | new_roadbits) != new_roadbits) {
						/* There is already a foundation on the tile, or at least
						 * another slope that is not compatible with the new one. */
						return 0;
					}
					/* If the start is in the low part, it is automatically
					 * building the second part too. */
					return ((start_roadbits & ROAD_NE) && !(existing_roadbits & ROAD_SW)) ? 2 : 1;

				default:
					/* Roadbits causing a foundation are going to be build.
					 * When the existing roadbits are slopes (the lower bits
					 * are used), this cannot be done. */
					return (existing_roadbits & ROAD_NE) ? 0 : 1;
			}

		default:
			NOT_REACHED();
	}
}