changeset 6012:542153c1c803 draft

(svn r8735) -Feature: drive-through road stops made possible by the hard work of mart3p.
author rubidium <rubidium@openttd.org>
date Wed, 14 Feb 2007 16:37:16 +0000
parents 21d301b3bd81
children 69b46559a66b
files bin/data/roadstops.grf src/command.cpp src/command.h src/gfxinit.cpp src/lang/english.txt src/lang/hungarian.txt src/npf.cpp src/pathfind.cpp src/road_gui.cpp src/road_map.cpp src/roadveh_cmd.cpp src/saveload.cpp src/settings.cpp src/settings_gui.cpp src/station.cpp src/station.h src/station_cmd.cpp src/station_map.h src/table/sprites.h src/table/station_land.h src/variables.h src/vehicle.cpp src/vehicle.h src/yapf/follow_track.hpp src/yapf/yapf_road.cpp src/yapf/yapf_settings.h
diffstat 26 files changed, 373 insertions(+), 61 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1a243ef6e358f4d7d379a95ebcdc7be499e0c38f
GIT binary patch
literal 2403
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
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -45,6 +45,7 @@
 DEF_COMMAND(CmdRemoveTrainWaypoint);
 
 DEF_COMMAND(CmdBuildRoadStop);
+DEF_COMMAND(CmdRemoveRoadStop);
 
 DEF_COMMAND(CmdBuildLongRoad);
 DEF_COMMAND(CmdRemoveLongRoad);
@@ -188,7 +189,7 @@
 	{NULL,                                   0}, /*  19 */
 	{NULL,                                   0}, /*  20 */
 	{CmdBuildRoadStop,                       0}, /*  21 */
-	{NULL,                                   0}, /*  22 */
+	{CmdRemoveRoadStop,                      0}, /*  22 */
 	{CmdBuildLongRoad,                       0}, /*  23 */
 	{CmdRemoveLongRoad,                      0}, /*  24 */
 	{CmdBuildRoad,                           0}, /*  25 */
--- a/src/command.h
+++ b/src/command.h
@@ -27,6 +27,7 @@
 	CMD_REMOVE_TRAIN_WAYPOINT        =  18,
 
 	CMD_BUILD_ROAD_STOP              =  21,
+	CMD_REMOVE_ROAD_STOP             =  22,
 	CMD_BUILD_LONG_ROAD              =  23,
 	CMD_REMOVE_LONG_ROAD             =  24,
 	CMD_BUILD_ROAD                   =  25,
--- a/src/gfxinit.cpp
+++ b/src/gfxinit.cpp
@@ -387,6 +387,9 @@
 	assert(load_index == SPR_AIRPORTX_BASE);
 	load_index += LoadGrfFile("airports.grf", load_index, i++);
 
+	assert(load_index == SPR_ROADSTOP_BASE);
+	load_index += LoadGrfFile("roadstops.grf", load_index, i++);
+
 	/* Initialize the unicode to sprite mapping table */
 	InitializeUnicodeGlyphMap();
 
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1041,6 +1041,7 @@
 STR_CONFIG_PATCHES_NONUNIFORM_STATIONS                          :{LTBLUE}Nonuniform stations: {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_NEW_PATHFINDING_ALL                          :{LTBLUE}New global pathfinding (NPF, overrides NTP): {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_FREIGHT_TRAINS                               :{LTBLUE}Weight multiplier for freight to simulate heavy trains: {ORANGE}{STRING}
+STR_CONFIG_PATCHES_STOP_ON_TOWN_ROAD                            :{LTBLUE}Allow drive-through road stops on town owned roads: {ORANGE}{STRING}
 
 STR_CONFIG_PATCHES_SMALL_AIRPORTS                               :{LTBLUE}Always allow small airports: {ORANGE}{STRING1}
 
@@ -1584,6 +1585,8 @@
 STR_1816_TREE_LINED_ROAD                                        :Tree-lined road
 STR_1817_ROAD_VEHICLE_DEPOT                                     :Road vehicle depot
 STR_1818_ROAD_RAIL_LEVEL_CROSSING                               :Road/rail level crossing
+STR_CAN_T_REMOVE_BUS_STATION                                    :{WHITE}Can't remove bus station...
+STR_CAN_T_REMOVE_TRUCK_STATION                                  :{WHITE}Can't remove lorry station...
 
 ##id 0x2000
 STR_2000_TOWNS                                                  :{WHITE}Towns
--- a/src/lang/hungarian.txt
+++ b/src/lang/hungarian.txt
@@ -1107,6 +1107,7 @@
 STR_CONFIG_PATCHES_NONUNIFORM_STATIONS                          :{LTBLUE}Különböző vágánytípusok engedélyezése egy állomáson: {ORANGE}{STRING}
 STR_CONFIG_PATCHES_NEW_PATHFINDING_ALL                          :{LTBLUE}Új útvonalkereső (NPF, felülbírálja az NTP-t): {ORANGE}{STRING}
 STR_CONFIG_PATCHES_FREIGHT_TRAINS                               :{LTBLUE}Tömegszorzó tehervonatoknak (szimulációs célból): {ORANGE}{STRING}
+STR_CONFIG_PATCHES_STOP_ON_TOWN_ROAD                            :{LTBLUE}Áthajtható állomások engedélyezése városi utakra: {ORANGE}{STRING}
 
 STR_CONFIG_PATCHES_SMALL_AIRPORTS                               :{LTBLUE}Mindig engedélyezze a kis repülőtereket: {ORANGE}{STRING}
 
@@ -1650,6 +1651,8 @@
 STR_1816_TREE_LINED_ROAD                                        :Fával szegélyezett út
 STR_1817_ROAD_VEHICLE_DEPOT                                     :Garázs
 STR_1818_ROAD_RAIL_LEVEL_CROSSING                               :Út/vasút kereszteződés
+STR_CAN_T_REMOVE_BUS_STATION                                    :{WHITE}Nem távolíthatod el ezt a buszmegállót...
+STR_CAN_T_REMOVE_TRUCK_STATION                                  :{WHITE}Nem távolíthatod el ezt a teherautó megállót...
 
 ##id 0x2000
 STR_2000_TOWNS                                                  :{WHITE}Városok
--- a/src/npf.cpp
+++ b/src/npf.cpp
@@ -279,6 +279,12 @@
 			if (IsLevelCrossing(tile)) cost += _patches.npf_crossing_penalty;
 			break;
 
+		case MP_STATION:
+			cost = NPF_TILE_LENGTH;
+			/* Increase the cost for drive-through road stops */
+			if (IsDriveThroughStopTile(tile)) cost += _patches.npf_road_drive_through_penalty;
+			break;
+
 		default:
 			break;
 	}
@@ -453,7 +459,7 @@
 	if (IsTileType(tile, MP_RAILWAY) ||           /* Rail tile (also rail depot) */
 			IsRailwayStationTile(tile) ||               /* Rail station tile */
 			IsTileDepotType(tile, TRANSPORT_ROAD) ||  /* Road depot tile */
-			IsRoadStopTile(tile) ||                /* Road station tile */
+			IsStandardRoadStopTile(tile) || /* Road station tile (but not drive-through stops) */
 			IsTileDepotType(tile, TRANSPORT_WATER)) { /* Water depot tile */
 		return IsTileOwner(tile, owner); /* You need to own these tiles entirely to use them */
 	}
@@ -529,8 +535,8 @@
 	} else if (IsBridgeTile(src_tile) && GetBridgeRampDirection(src_tile) == src_exitdir) {
 		dst_tile = GetOtherBridgeEnd(src_tile);
 		override_dst_check = true;
-	} else if (type != TRANSPORT_WATER && (IsRoadStopTile(src_tile) || IsTileDepotType(src_tile, type))) {
-		/* This is a road station or a train or road depot. We can enter and exit
+	} else if (type != TRANSPORT_WATER && (IsStandardRoadStopTile(src_tile) || IsTileDepotType(src_tile, type))) {
+		/* This is a road station (non drive-through) or a train or road depot. We can enter and exit
 		 * those from one side only. Trackdirs don't support that (yet), so we'll
 		 * do this here. */
 
@@ -599,7 +605,7 @@
 	}
 
 	/* Determine available tracks */
-	if (type != TRANSPORT_WATER && (IsRoadStopTile(dst_tile) || IsTileDepotType(dst_tile, type))){
+	if (type != TRANSPORT_WATER && (IsStandardRoadStopTile(dst_tile) || IsTileDepotType(dst_tile, type))){
 		/* Road stations and road and train depots return 0 on GTTS, so we have to do this by hand... */
 		DiagDirection exitdir;
 		if (IsRoadStopTile(dst_tile)) {
--- a/src/pathfind.cpp
+++ b/src/pathfind.cpp
@@ -296,7 +296,7 @@
 	if (tpf->tracktype == TRANSPORT_ROAD) {
 		// road stops and depots now have a track (r4419)
 		// don't enter road stop from the back
-		if (IsRoadStopTile(tile) && ReverseDiagDir(GetRoadStopDir(tile)) != direction) return;
+		if (IsStandardRoadStopTile(tile) && ReverseDiagDir(GetRoadStopDir(tile)) != direction) return;
 		// don't enter road depot from the back
 		if (IsTileDepotType(tile, TRANSPORT_ROAD) && ReverseDiagDir(GetRoadDepotDirection(tile)) != direction) return;
 	}
--- a/src/road_gui.cpp
+++ b/src/road_gui.cpp
@@ -16,6 +16,7 @@
 #include "sound.h"
 #include "command.h"
 #include "variables.h"
+#include "station_map.h"
 //needed for catchments
 #include "station.h"
 
@@ -83,7 +84,7 @@
 	if (success) {
 		SndPlayTileFx(SND_1F_SPLAT, tile);
 		ResetObjectToPlace();
-		BuildRoadOutsideStation(tile, (DiagDirection)p1);
+		if (!HASBIT(p2, 1)) BuildRoadOutsideStation(tile, (DiagDirection)p1);
 	}
 }
 
@@ -92,14 +93,45 @@
 	DoCommandP(tile, _road_depot_orientation, 0, CcRoadDepot, CMD_BUILD_ROAD_DEPOT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1807_CAN_T_BUILD_ROAD_VEHICLE));
 }
 
+static void PlaceRoadStop(TileIndex tile, uint32 p2, uint32 cmd)
+{
+	uint32 p1 = _road_station_picker_orientation;
+
+	if (p1 >= DIAGDIR_END) {
+		SETBIT(p2, 1); // It's a drive-through stop
+		p1 -= DIAGDIR_END; // Adjust picker result to actual direction
+
+		/* Only allow building over a road if its a straight road,
+		 * facing the right direction and it belongs to the player */
+		if ((IsTileType(tile, MP_STREET) &&
+				GetRoadTileType(tile) == ROAD_TILE_NORMAL &&
+				(IsTileOwner(tile, _current_player) || (_patches.road_stop_on_town_road && IsTileOwner(tile, OWNER_TOWN))) &&
+				!(GetRoadBits(tile) & ((DiagDirection)p1 == DIAGDIR_NE ? ROAD_Y : ROAD_X)))) {
+
+			cmd ^= CMD_AUTO;
+			SETBIT(p2, 2); // We're building over an existing road
+			if (IsTileOwner(tile, OWNER_TOWN)) SETBIT(p2, 3); // It's a town owned road
+		}
+	}
+	DoCommandP(tile, p1, p2, CcRoadDepot, cmd);
+}
+
 static void PlaceRoad_BusStation(TileIndex tile)
 {
-	DoCommandP(tile, _road_station_picker_orientation, RoadStop::BUS, CcRoadDepot, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION));
+	if (_remove_button_clicked) {
+		DoCommandP(tile, 0, RoadStop::BUS, CcPlaySound1D, CMD_REMOVE_ROAD_STOP | CMD_MSG(STR_CAN_T_REMOVE_BUS_STATION));
+	} else {
+		PlaceRoadStop(tile, RoadStop::BUS, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION));
+	}
 }
 
 static void PlaceRoad_TruckStation(TileIndex tile)
 {
-	DoCommandP(tile, _road_station_picker_orientation, RoadStop::TRUCK, CcRoadDepot, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION));
+	if (_remove_button_clicked) {
+		DoCommandP(tile, 0, RoadStop::TRUCK, CcPlaySound1D, CMD_REMOVE_ROAD_STOP | CMD_MSG(STR_CAN_T_REMOVE_TRUCK_STATION));
+	} else {
+		PlaceRoadStop(tile, RoadStop::TRUCK, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION));
+	}
 }
 
 static void PlaceRoad_DemolishArea(TileIndex tile)
@@ -195,7 +227,7 @@
 	case WE_CREATE: DisableWindowWidget(w, RTW_REMOVE); break;
 
 	case WE_PAINT:
-		if (IsWindowWidgetLowered(w, RTW_ROAD_X) || IsWindowWidgetLowered(w, RTW_ROAD_Y)) {
+		if (IsWindowWidgetLowered(w, RTW_ROAD_X) || IsWindowWidgetLowered(w, RTW_ROAD_Y) || IsWindowWidgetLowered(w, RTW_BUS_STATION) || IsWindowWidgetLowered(w, RTW_TRUCK_STATION)) {
 			EnableWindowWidget(w, RTW_REMOVE);
 		}
 		DrawWindowWidgets(w);
@@ -428,7 +460,7 @@
 	switch (e->event) {
 	case WE_CREATE:
 		LowerWindowWidget(w, _road_station_picker_orientation + 3);
-		LowerWindowWidget(w, _station_show_coverage + 7);
+		LowerWindowWidget(w, _station_show_coverage + 9);
 		break;
 
 	case WE_PAINT: {
@@ -445,13 +477,18 @@
 			SetTileSelectSize(1, 1);
 		}
 
-		image = (w->window_class == WC_BUS_STATION) ? 0x47 : 0x43;
+		image = (w->window_class == WC_BUS_STATION) ? GFX_BUS_BASE : GFX_TRUCK_BASE;
 
 		StationPickerDrawSprite(103, 35, RAILTYPE_BEGIN, image);
 		StationPickerDrawSprite(103, 85, RAILTYPE_BEGIN, image+1);
 		StationPickerDrawSprite(35, 85, RAILTYPE_BEGIN, image+2);
 		StationPickerDrawSprite(35, 35, RAILTYPE_BEGIN, image+3);
 
+		image = (w->window_class == WC_BUS_STATION) ? GFX_BUS_BASE_EXT : GFX_TRUCK_BASE_EXT;
+
+		StationPickerDrawSprite(171, 35, RAILTYPE_BEGIN, image);
+		StationPickerDrawSprite(171, 85, RAILTYPE_BEGIN, image + 1);
+
 		DrawStationCoverageAreaText(2, 146,
 			((w->window_class == WC_BUS_STATION) ? (1<<CT_PASSENGERS) : ~(1<<CT_PASSENGERS)),
 			3);
@@ -460,17 +497,17 @@
 
 	case WE_CLICK: {
 		switch (e->we.click.widget) {
-		case 3: case 4: case 5: case 6:
+		case 3: case 4: case 5: case 6: case 7: case 8:
 			RaiseWindowWidget(w, _road_station_picker_orientation + 3);
 			_road_station_picker_orientation = (DiagDirection)(e->we.click.widget - 3);
 			LowerWindowWidget(w, _road_station_picker_orientation + 3);
 			SndPlayFx(SND_15_BEEP);
 			SetWindowDirty(w);
 			break;
-		case 7: case 8:
-			RaiseWindowWidget(w, _station_show_coverage + 7);
-			_station_show_coverage = (e->we.click.widget != 7);
-			LowerWindowWidget(w, _station_show_coverage + 7);
+		case 9: case 10:
+			RaiseWindowWidget(w, _station_show_coverage + 9);
+			_station_show_coverage = (e->we.click.widget != 9);
+			LowerWindowWidget(w, _station_show_coverage + 9);
 			SndPlayFx(SND_15_BEEP);
 			SetWindowDirty(w);
 			break;
@@ -494,12 +531,14 @@
 
 static const Widget _bus_station_picker_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   139,     0,    13, STR_3042_BUS_STATION_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
-{      WWT_PANEL,   RESIZE_NONE,     7,     0,   139,    14,   176, 0x0,                              STR_NULL},
+{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   206,     0,    13, STR_3042_BUS_STATION_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,   RESIZE_NONE,     7,     0,   206,    14,   176, 0x0,                              STR_NULL},
 {      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    17,    66, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
 {      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    69,   118, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
 {      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    69,   118, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
 {      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    17,    66, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
+{      WWT_PANEL,   RESIZE_NONE,    14,   139,   204,    17,    66, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
+{      WWT_PANEL,   RESIZE_NONE,    14,   139,   204,    69,   118, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    10,    69,   133,   144, STR_02DB_OFF,                     STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    70,   129,   133,   144, STR_02DA_ON,                      STR_3064_HIGHLIGHT_COVERAGE_AREA},
 {      WWT_LABEL,   RESIZE_NONE,     7,     0,   139,   120,   133, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
@@ -507,7 +546,7 @@
 };
 
 static const WindowDesc _bus_station_picker_desc = {
-	WDP_AUTO, WDP_AUTO, 140, 177,
+	WDP_AUTO, WDP_AUTO, 207, 177,
 	WC_BUS_STATION, WC_BUILD_TOOLBAR,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 	_bus_station_picker_widgets,
@@ -521,12 +560,14 @@
 
 static const Widget _truck_station_picker_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   139,     0,    13, STR_3043_TRUCK_STATION_ORIENT,    STR_018C_WINDOW_TITLE_DRAG_THIS},
-{      WWT_PANEL,   RESIZE_NONE,     7,     0,   139,    14,   176, 0x0,                              STR_NULL},
+{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   206,     0,    13, STR_3043_TRUCK_STATION_ORIENT,    STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,   RESIZE_NONE,     7,     0,   206,    14,   176, 0x0,                              STR_NULL},
 {      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    17,    66, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
 {      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    69,   118, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
 {      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    69,   118, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
 {      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    17,    66, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
+{      WWT_PANEL,   RESIZE_NONE,    14,   139,   204,    17,    66, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
+{      WWT_PANEL,   RESIZE_NONE,    14,   139,   204,    69,   118, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    10,    69,   133,   144, STR_02DB_OFF,                     STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    70,   129,   133,   144, STR_02DA_ON,                      STR_3064_HIGHLIGHT_COVERAGE_AREA},
 {      WWT_LABEL,   RESIZE_NONE,     7,     0,   139,   120,   133, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
@@ -534,7 +575,7 @@
 };
 
 static const WindowDesc _truck_station_picker_desc = {
-	WDP_AUTO, WDP_AUTO, 140, 177,
+	WDP_AUTO, WDP_AUTO, 207, 177,
 	WC_TRUCK_STATION, WC_BUILD_TOOLBAR,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 	_truck_station_picker_widgets,
--- a/src/road_map.cpp
+++ b/src/road_map.cpp
@@ -24,6 +24,7 @@
 
 		case MP_STATION:
 			if (!IsRoadStopTile(tile)) return ROAD_NONE;
+			if (IsDriveThroughStopTile(tile)) return (GetRoadStopDir(tile) == DIAGDIR_NE) ? ROAD_X : ROAD_Y;
 			return DiagDirToRoadBits(GetRoadStopDir(tile));
 
 		case MP_TUNNELBRIDGE:
@@ -45,7 +46,7 @@
 	uint32 r;
 
 	// Don't allow local authorities to build roads through road depots or road stops.
-	if ((IsTileType(tile, MP_STREET) && IsTileDepotType(tile, TRANSPORT_ROAD)) || IsTileType(tile, MP_STATION)) {
+	if ((IsTileType(tile, MP_STREET) && IsTileDepotType(tile, TRANSPORT_ROAD)) || (IsTileType(tile, MP_STATION) && !IsDriveThroughStopTile(tile))) {
 		return TRACK_BIT_NONE;
 	}
 
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -1080,7 +1080,8 @@
 			/* Road depot owned by another player or with the wrong orientation */
 			trackdirs = TRACKDIR_BIT_NONE;
 		}
-	} else if (IsTileType(tile, MP_STATION) && IsRoadStopTile(tile)) {
+	} else if (IsTileType(tile, MP_STATION) && IsStandardRoadStopTile(tile)) {
+		/* Standard road stop (drive-through stops are treated as normal road) */
 		if (!IsTileOwner(tile, v->owner) || GetRoadStopDir(tile) == enterdir) {
 			/* different station owner or wrong orientation */
 			trackdirs = TRACKDIR_BIT_NONE;
@@ -1093,7 +1094,7 @@
 				trackdirs = TRACKDIR_BIT_NONE;
 			} else {
 				/* Proper station type, check if there is free loading bay */
-				if (!_patches.roadveh_queue &&
+				if (!_patches.roadveh_queue &&  IsStandardRoadStopTile(tile) &&
 						!GetRoadStopByTile(tile, rstype)->HasFreeBay()) {
 					/* Station is full and RV queuing is off */
 					trackdirs = TRACKDIR_BIT_NONE;
@@ -1167,7 +1168,8 @@
 				goto do_it;
 			}
 		} else if (IsTileType(desttile, MP_STATION)) {
-			if (IsRoadStop(desttile)) {
+			/* For drive-through stops we can head for the actual station tile */
+			if (IsStandardRoadStopTile(desttile)) {
 				dir = GetRoadStopDir(desttile);
 do_it:;
 				/* When we are heading for a depot or station, we just
@@ -1242,9 +1244,11 @@
 	/* Start frames for when a vehicle enters a tile/changes its state.
 	 * The start frame is different for vehicles that turned around or
 	 * are leaving the depot as the do not start at the edge of the tile */
-	RVC_DEFAULT_START_FRAME     = 0,
-	RVC_TURN_AROUND_START_FRAME = 1,
-	RVC_DEPOT_START_FRAME       = 6
+	RVC_DEFAULT_START_FRAME      = 0,
+	RVC_TURN_AROUND_START_FRAME  = 1,
+	RVC_DEPOT_START_FRAME        = 6,
+	/* Stop frame for a vehicle in a drive-through stop */
+	RVC_DRIVE_THROUGH_STOP_FRAME = 7
 };
 
 typedef struct RoadDriveEntry {
@@ -1376,8 +1380,12 @@
 		return;
 	}
 
-	/* Get move position data for next frame */
-	rd = _road_drive_data[(v->u.road.state + (_opt.road_side << RVS_DRIVE_SIDE)) ^ v->u.road.overtaking][v->u.road.frame + 1];
+	/* Get move position data for next frame.
+	 * For a drive-through road stop use 'straight road' move data.
+	 * In this case v->u.road.state is masked to give the road stop entry direction. */
+	rd = _road_drive_data[(
+		(HASBIT(v->u.road.state, RVS_IN_DT_ROAD_STOP) ? v->u.road.state & RVSB_ROAD_STOP_TRACKDIR_MASK : v->u.road.state) +
+		(_opt.road_side << RVS_DRIVE_SIDE)) ^ v->u.road.overtaking][v->u.road.frame + 1];
 
 	if (rd.x & RDE_NEXT_TILE) {
 		TileIndex tile = v->tile + TileOffsByDiagDir(rd.x & 3);
@@ -1417,8 +1425,8 @@
 			goto again;
 		}
 
-		if (IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END) && IsTileType(v->tile, MP_STATION)) {
-			if (IsReversingRoadTrackdir(dir)) {
+		if (IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && IsTileType(v->tile, MP_STATION)) {
+			if (IsReversingRoadTrackdir(dir) && IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) {
 				/* New direction is trying to turn vehicle around.
 				 * We can't turn at the exit of a road stop so wait.*/
 				v->cur_speed = 0;
@@ -1427,9 +1435,13 @@
 			if (IsRoadStop(v->tile)) {
 				RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
 
-				/* Vehicle is leaving a road stop tile, mark bay as free */
-				rs->FreeBay(HASBIT(v->u.road.state, RVS_USING_SECOND_BAY));
-				rs->SetEntranceBusy(false);
+				/* Vehicle is leaving a road stop tile, mark bay as free
+				 * For drive-through stops, only do it if the vehicle stopped here */
+				if (IsStandardRoadStopTile(v->tile) || HASBIT(v->u.road.state, RVS_IS_STOPPING)) {
+					rs->FreeBay(HASBIT(v->u.road.state, RVS_USING_SECOND_BAY));
+					CLRBIT(v->u.road.state, RVS_IS_STOPPING);
+				}
+				if (IsStandardRoadStopTile(v->tile)) rs->SetEntranceBusy(false);
 			}
 		}
 
@@ -1523,8 +1535,18 @@
 		}
 	}
 
-	if (v->u.road.state >= RVSB_IN_ROAD_STOP &&
-			_road_veh_data_1[v->u.road.state - RVSB_IN_ROAD_STOP + (_opt.road_side << RVS_DRIVE_SIDE)] == v->u.road.frame) {
+	/* If the vehicle is in a normal road stop and the frame equals the stop frame OR
+	 * if the vehicle is in a drive-through road stop and this is the destination station
+	 * and it's the correct type of stop (bus or truck) and the frame equals the stop frame...
+	 * (the station test and stop type test ensure that other vehicles, using the road stop as
+	 * a through route, do not stop) */
+	if ((IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END) &&
+			_road_veh_data_1[v->u.road.state - RVSB_IN_ROAD_STOP + (_opt.road_side << RVS_DRIVE_SIDE)] == v->u.road.frame) ||
+			(IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
+			v->current_order.dest == GetStationIndex(v->tile) &&
+			GetRoadStopType(v->tile) == ((v->cargo_type == CT_PASSENGERS) ? RoadStop::BUS : RoadStop::TRUCK) &&
+			v->u.road.frame == RVC_DRIVE_THROUGH_STOP_FRAME)) {
+
 		RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
 		Station* st = GetStationByTile(v->tile);
 
@@ -1536,6 +1558,31 @@
 			/* Vehicle has arrived at a bay in a road stop */
 			Order old_order;
 
+			if (IsDriveThroughStopTile(v->tile)) {
+				TileIndex next_tile = TILE_ADD(v->tile, TileOffsByDir(v->direction));
+				RoadStop::Type type = (v->cargo_type == CT_PASSENGERS) ? RoadStop::BUS : RoadStop::TRUCK;
+
+				assert(HASBIT(v->u.road.state, RVS_IS_STOPPING));
+
+				/* Check if next inline bay is free */
+				if (IsDriveThroughStopTile(next_tile) && (GetRoadStopType(next_tile) == type)) {
+					RoadStop *rs_n = GetRoadStopByTile(next_tile, type);
+
+					if (rs_n->IsFreeBay(HASBIT(v->u.road.state, RVS_USING_SECOND_BAY))) {
+						/* Bay in next stop along is free - use it */
+						ClearSlot(v);
+						rs_n->num_vehicles++;
+						v->u.road.slot = rs_n;
+						v->dest_tile = rs_n->xy;
+						v->u.road.slot_age = 14;
+
+						v->u.road.frame++;
+						RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
+						return;
+					}
+				}
+			}
+
 			rs->SetEntranceBusy(false);
 
 			v->last_station_visited = GetStationIndex(v->tile);
@@ -1573,7 +1620,7 @@
 			ClearSlot(v);
 		}
 
-		rs->SetEntranceBusy(true);
+		if (IsStandardRoadStopTile(v->tile)) rs->SetEntranceBusy(true);
 
 		if (rs == v->u.road.slot) {
 			/* We are leaving the correct station */
--- a/src/saveload.cpp
+++ b/src/saveload.cpp
@@ -30,7 +30,7 @@
 #include "variables.h"
 #include <setjmp.h>
 
-extern const uint16 SAVEGAME_VERSION = 46;
+extern const uint16 SAVEGAME_VERSION = 47;
 uint16 _sl_version;       /// the major savegame version identifier
 byte   _sl_minor_version; /// the minor savegame version, DO NOT USE!
 
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1340,6 +1340,7 @@
 	SDT_BOOL(Patches, serviceathelipad,        0, 0,  true,        STR_CONFIG_PATCHES_SERVICEATHELIPAD,   NULL),
 	SDT_BOOL(Patches, modified_catchment,      0, 0,  true,        STR_CONFIG_PATCHES_CATCHMENT,          NULL),
 	SDT_CONDBOOL(Patches, gradual_loading, 40, SL_MAX_VERSION, 0, 0,  true, STR_CONFIG_PATCHES_GRADUAL_LOADING,    NULL),
+	SDT_CONDBOOL(Patches, road_stop_on_town_road, 47, SL_MAX_VERSION, 0, 0, false, STR_CONFIG_PATCHES_STOP_ON_TOWN_ROAD, NULL),
 
 	/***************************************************************************/
 	/* Economy section of the GUI-configure patches window */
@@ -1431,6 +1432,8 @@
 	SDT_VAR(Patches, npf_road_curve_penalty,        SLE_UINT, 0, 0, 1,                      0, 100000, 0, STR_NULL, NULL),
 	/* This is the penalty for level crossings, for both road and rail vehicles */
 	SDT_VAR(Patches, npf_crossing_penalty,          SLE_UINT, 0, 0, (3 * NPF_TILE_LENGTH),  0, 100000, 0, STR_NULL, NULL),
+	/* This is the penalty for drive-through road, stops. */
+	SDT_CONDVAR (Patches, npf_road_drive_through_penalty, SLE_UINT, 47, SL_MAX_VERSION, 0, 0,  8 * NPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
 
 
 	// The maximum number of nodes to search
@@ -1464,6 +1467,7 @@
 	SDT_CONDVAR (Patches, yapf.road_slope_penalty                    , SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  2 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
 	SDT_CONDVAR (Patches, yapf.road_curve_penalty                    , SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  1 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
 	SDT_CONDVAR (Patches, yapf.road_crossing_penalty                 , SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
+	SDT_CONDVAR (Patches, yapf.road_stop_penalty                     , SLE_UINT, 47, SL_MAX_VERSION, 0, 0,  8 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
 
 	/***************************************************************************/
 	/* Terrain genation related patch options */
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -599,6 +599,7 @@
 	"serviceathelipad",
 	"modified_catchment",
 	"gradual_loading",
+	"road_stop_on_town_road",
 };
 
 static const char *_patches_economy[] = {
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -502,6 +502,13 @@
 	return GB(status, 0, MAX_BAY_COUNT) != 0;
 }
 
+/** Checks whether the given bay is free in this road stop */
+bool RoadStop::IsFreeBay(uint nr) const
+{
+	assert(nr < MAX_BAY_COUNT);
+	return HASBIT(status, nr);
+}
+
 /**
  * Allocates a bay
  * @return the allocated bay number
@@ -520,6 +527,16 @@
 }
 
 /**
+ * Allocates a bay in a drive-through road stop
+ * @param nr the number of the bay to allocate
+ */
+void RoadStop::AllocateDriveThroughBay(uint nr)
+{
+	assert(nr < MAX_BAY_COUNT);
+	CLRBIT(status, nr);
+}
+
+/**
  * Frees the given bay
  * @param nr the number of the bay to free
  */
--- a/src/station.h
+++ b/src/station.h
@@ -66,7 +66,9 @@
 
 	/* For accessing status */
 	bool HasFreeBay() const;
+	bool IsFreeBay(uint nr) const;
 	uint AllocateBay();
+	void AllocateDriveThroughBay(uint nr);
 	void FreeBay(uint nr);
 	bool IsEntranceBusy() const;
 	void SetEntranceBusy(bool busy);
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -35,6 +35,7 @@
 #include "date.h"
 #include "helpers.hpp"
 #include "misc/autoptr.hpp"
+#include "road.h"
 
 /**
  * Called if a new block is added to the station-pool
@@ -1247,7 +1248,10 @@
 /** Build a bus or truck stop
  * @param tile tile to build the stop at
  * @param p1 entrance direction (DiagDirection)
- * @param p2 0 for Bus stops, 1 for truck stops
+ * @param p2 bit 0: 0 for Bus stops, 1 for truck stops
+ *           bit 1: 0 for normal, 1 for drive-through
+ *           bit 2: 0 for normal, 1 for build over road
+ *           bit 3: 0 for player owned road, 1 for town owned road
  */
 int32 CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
@@ -1255,19 +1259,29 @@
 	RoadStop *road_stop;
 	int32 cost;
 	int32 ret;
-	bool type = !!p2;
+	bool type = HASBIT(p2, 0);
+	bool is_drive_through = HASBIT(p2, 1);
+	Owner cur_owner = _current_player;
 
 	/* 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;
+	/* If overbuilding a road check tile is a valid road tile */
+	if (HASBIT(p2, 2) && !(IsTileType(tile, MP_STREET) && GetRoadTileType(tile) == ROAD_TILE_NORMAL)) return CMD_ERROR;
+	/* If overbuilding a town road,check tile is town owned and patch setting is enabled */
+	if (HASBIT(p2, 3) && !(_patches.road_stop_on_town_road && IsTileOwner(tile, OWNER_TOWN))) return CMD_ERROR;
 
 	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 
 	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
 		return CMD_ERROR;
 
-	ret = CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL);
+	if (is_drive_through & HASBIT(p2, 3)) _current_player = OWNER_TOWN;
+	ret = CheckFlatLandBelow(tile, 1, 1, flags, is_drive_through ? 5 << p1 : 1 << p1, NULL);
+	_current_player = cur_owner;
 	if (CmdFailed(ret)) return ret;
-	cost = ret;
+	cost = HASBIT(p2, 2) ? 0 : ret; // Don't add cost of clearing road when overbuilding
 
 	st = GetStationAround(tile, 1, 1, INVALID_STATION);
 	if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
@@ -1333,7 +1347,8 @@
 
 		st->rect.BeforeAddTile(tile, StationRect::ADD_TRY);
 
-		MakeRoadStop(tile, st->owner, st->index, type ? RoadStop::TRUCK : RoadStop::BUS, (DiagDirection)p1);
+		MakeRoadStop(tile, st->owner, st->index, type ? RoadStop::TRUCK : RoadStop::BUS, is_drive_through, (DiagDirection)p1);
+		if (is_drive_through & HASBIT(p2, 3)) SetStopBuiltOnTownRoad(tile);
 
 		UpdateStationVirtCoordDirty(st);
 		UpdateStationAcceptance(st, false);
@@ -1395,7 +1410,44 @@
 	return (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 p1 not used
+ * @param p2 bit 0: 0 for Bus stops, 1 for truck stops
+ */
+int32 CmdRemoveRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	Station* st;
+	bool is_drive_through;
+	bool is_towns_road = false;
+	RoadBits road_bits;
+	int32 ret;
+
+	/* Make sure the specified tile is a road stop of the correct type */
+	if (!IsTileType(tile, MP_STATION) || !IsRoadStop(tile) || (uint32)GetRoadStopType(tile) != p2) return CMD_ERROR;
+	st = GetStationByTile(tile);
+	/* Save the stop info before it is removed */
+	is_drive_through = IsDriveThroughStopTile(tile);
+	road_bits = GetAnyRoadBits(tile);
+	if (is_drive_through) is_towns_road = GetStopBuiltOnTownRoad(tile);
+
+	ret = RemoveRoadStop(st, flags, tile);
+
+	/* If the stop was a drive-through stop replace the road */
+	if ((flags & DC_EXEC) && !CmdFailed(ret) && is_drive_through) {
+		uint index = 0;
+		Owner cur_owner = _current_player;
+
+		if (is_towns_road) {
+			index = ClosestTownFromTile(tile, _patches.dist_local_authority)->index;
+			_current_player = OWNER_TOWN;
+		}
+		DoCommand(tile, road_bits, index, DC_EXEC, CMD_BUILD_ROAD);
+		_current_player = cur_owner;
+	}
+
+	return ret;
+}
 
 // FIXME -- need to move to its corresponding Airport variable
 // Country Airfield (small)
@@ -2227,6 +2279,27 @@
 				/* Attempt to allocate a parking bay in a road stop */
 				RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
 
+				if (IsDriveThroughStopTile(tile)) {
+					/* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
+					byte side = ((DirToDiagDir(v->direction) == ReverseDiagDir(GetRoadStopDir(tile))) == (v->u.road.overtaking == 0)) ? 0 : 1;
+
+					if (!rs->IsFreeBay(side)) return VETSB_CANNOT_ENTER;
+
+					/* Check if the vehicle is stopping at this road stop */
+					if (GetRoadStopType(tile) == ((v->cargo_type == CT_PASSENGERS) ? RoadStop::BUS : RoadStop::TRUCK) &&
+							v->current_order.dest == GetStationIndex(tile)) {
+						SETBIT(v->u.road.state, RVS_IS_STOPPING);
+						rs->AllocateDriveThroughBay(side);
+					}
+
+					/* Indicate if vehicle is using second bay. */
+					if (side == 1) SETBIT(v->u.road.state, RVS_USING_SECOND_BAY);
+					/* Indicate a drive-through stop */
+					SETBIT(v->u.road.state, RVS_IN_DT_ROAD_STOP);
+					return VETSB_CONTINUE;
+				}
+
+				/* For normal (non drive-through) road stops */
 				/* Check if station is busy or if there are no free bays. */
 				if (rs->IsEntranceBusy() || !rs->HasFreeBay()) return VETSB_CANNOT_ENTER;
 
@@ -2693,7 +2766,13 @@
 		case STATION_RAIL:    return RemoveRailroadStation(st, tile, flags);
 		case STATION_AIRPORT: return RemoveAirport(st, flags);
 		case STATION_TRUCK:
-		case STATION_BUS:     return RemoveRoadStop(st, flags, tile);
+			if (IsDriveThroughStopTile(tile) && GetStopBuiltOnTownRoad(tile))
+				return_cmd_error(STR_3047_MUST_DEMOLISH_TRUCK_STATION);
+			return RemoveRoadStop(st, flags, tile);
+		case STATION_BUS:
+			if (IsDriveThroughStopTile(tile) && GetStopBuiltOnTownRoad(tile))
+				return_cmd_error(STR_3046_MUST_DEMOLISH_BUS_STATION);
+			return RemoveRoadStop(st, flags, tile);
 		case STATION_BUOY:    return RemoveBuoy(st, flags);
 		case STATION_DOCK:    return RemoveDock(st, flags);
 		default: break;
--- a/src/station_map.h
+++ b/src/station_map.h
@@ -42,7 +42,9 @@
 	GFX_RADAR_DISTRICTWE_LAST     = 156,
 	GFX_WINDSACK_INTERCON_FIRST   = 164,
 	GFX_WINDSACK_INTERCON_LAST    = 167,
-	GFX_BASE_END                  = 168
+	GFX_TRUCK_BASE_EXT            = 168,
+	GFX_BUS_BASE_EXT              = 170,
+	GFX_BASE_END                  = 172
 };
 
 enum {
@@ -51,7 +53,9 @@
 	TRUCK_SIZE = GFX_BUS_BASE - GFX_TRUCK_BASE,
 	BUS_SIZE = GFX_OILRIG_BASE - GFX_BUS_BASE,
 	DOCK_SIZE_TOTAL = GFX_BUOY_BASE - GFX_DOCK_BASE,
-	AIRPORT_SIZE_EXTENDED = GFX_BASE_END - GFX_AIRPORT_BASE_EXTENDED
+	AIRPORT_SIZE_EXTENDED = GFX_TRUCK_BASE_EXT - GFX_AIRPORT_BASE_EXTENDED,
+	TRUCK_SIZE_EXT = GFX_BUS_BASE_EXT - GFX_TRUCK_BASE_EXT,
+	BUS_SIZE_EXT = GFX_BASE_END - GFX_BUS_BASE_EXT,
 };
 
 typedef enum HangarTiles {
@@ -125,12 +129,14 @@
 
 static inline bool IsTruckStop(TileIndex t)
 {
-	return IS_BYTE_INSIDE(GetStationGfx(t), GFX_TRUCK_BASE, GFX_TRUCK_BASE + TRUCK_SIZE);
+	return (IS_BYTE_INSIDE(GetStationGfx(t), GFX_TRUCK_BASE, GFX_TRUCK_BASE + TRUCK_SIZE)) ||
+		(IS_BYTE_INSIDE(GetStationGfx(t), GFX_TRUCK_BASE_EXT, GFX_TRUCK_BASE_EXT + TRUCK_SIZE_EXT));
 }
 
 static inline bool IsBusStop(TileIndex t)
 {
-	return IS_BYTE_INSIDE(GetStationGfx(t), GFX_BUS_BASE, GFX_BUS_BASE + BUS_SIZE);
+	return (IS_BYTE_INSIDE(GetStationGfx(t), GFX_BUS_BASE, GFX_BUS_BASE + BUS_SIZE)) ||
+		(IS_BYTE_INSIDE(GetStationGfx(t), GFX_BUS_BASE_EXT, GFX_BUS_BASE_EXT + BUS_SIZE_EXT));
 }
 
 static inline bool IsRoadStop(TileIndex t)
@@ -143,13 +149,44 @@
 	return IsTileType(t, MP_STATION) && IsRoadStop(t);
 }
 
+static inline bool IsStandardRoadStopTile(TileIndex t)
+{
+	return IsTileType(t, MP_STATION) &&
+		(IS_BYTE_INSIDE(GetStationGfx(t), GFX_TRUCK_BASE, GFX_TRUCK_BASE + TRUCK_SIZE) ||
+		IS_BYTE_INSIDE(GetStationGfx(t), GFX_BUS_BASE, GFX_BUS_BASE + BUS_SIZE));
+}
+
+static inline bool IsDriveThroughStopTile(TileIndex t)
+{
+	return IsTileType(t, MP_STATION) &&
+		(IS_BYTE_INSIDE(GetStationGfx(t), GFX_TRUCK_BASE_EXT, GFX_TRUCK_BASE_EXT + TRUCK_SIZE_EXT) ||
+		IS_BYTE_INSIDE(GetStationGfx(t), GFX_BUS_BASE_EXT, GFX_BUS_BASE_EXT + BUS_SIZE_EXT));
+}
+
+static inline bool GetStopBuiltOnTownRoad(TileIndex t)
+{
+	assert(IsDriveThroughStopTile(t));
+	return HASBIT(_m[t].m6, 3);
+}
+
+static inline void SetStopBuiltOnTownRoad(TileIndex t)
+{
+	assert(IsDriveThroughStopTile(t));
+	SETBIT(_m[t].m6, 3);
+}
+
 /**
  * Gets the direction the road stop entrance points towards.
  */
 static inline DiagDirection GetRoadStopDir(TileIndex t)
 {
+	StationGfx gfx = GetStationGfx(t);
 	assert(IsRoadStopTile(t));
-	return (DiagDirection)((GetStationGfx(t) - GFX_TRUCK_BASE) & 3);
+	if (gfx < GFX_TRUCK_BASE_EXT) {
+		return (DiagDirection)((gfx - GFX_TRUCK_BASE) & 3);
+	} else {
+		return (DiagDirection)((gfx - GFX_TRUCK_BASE_EXT) & 1);
+	}
 }
 
 static inline bool IsOilRig(TileIndex t)
@@ -275,9 +312,13 @@
 	SetRailType(t, rt);
 }
 
-static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStop::Type rst, DiagDirection d)
+static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStop::Type rst, bool is_drive_through, DiagDirection d)
 {
-	MakeStation(t, o, sid, (rst == RoadStop::BUS ? GFX_BUS_BASE : GFX_TRUCK_BASE) + d);
+	if (is_drive_through) {
+		MakeStation(t, o, sid, (rst == RoadStop::BUS ? GFX_BUS_BASE_EXT : GFX_TRUCK_BASE_EXT) + d);
+	} else {
+		MakeStation(t, o, sid, (rst == RoadStop::BUS ? GFX_BUS_BASE : GFX_TRUCK_BASE) + d);
+	}
 }
 
 static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section)
--- a/src/table/sprites.h
+++ b/src/table/sprites.h
@@ -117,6 +117,16 @@
 	SPR_GRASS_RIGHT = SPR_AIRPORTX_BASE + 13,
 	SPR_GRASS_LEFT = SPR_AIRPORTX_BASE + 14,
 
+	SPR_ROADSTOP_BASE = SPR_AIRPORTX_BASE + 15, // The sprites used for drive-through road stops
+	SPR_BUS_STOP_DT_Y_W = SPR_ROADSTOP_BASE,
+	SPR_BUS_STOP_DT_Y_E = SPR_ROADSTOP_BASE + 1,
+	SPR_BUS_STOP_DT_X_W = SPR_ROADSTOP_BASE + 2,
+	SPR_BUS_STOP_DT_X_E = SPR_ROADSTOP_BASE + 3,
+	SPR_TRUCK_STOP_DT_Y_W = SPR_ROADSTOP_BASE + 4,
+	SPR_TRUCK_STOP_DT_Y_E = SPR_ROADSTOP_BASE + 5,
+	SPR_TRUCK_STOP_DT_X_W = SPR_ROADSTOP_BASE + 6,
+	SPR_TRUCK_STOP_DT_X_E = SPR_ROADSTOP_BASE + 7,
+
 	/* Manager face sprites */
 	SPR_GRADIENT = 874, // background gradient behind manager face
 
@@ -295,6 +305,10 @@
 	SPR_PYLON_NS_W = SPR_ELRAIL_BASE + 37,
 	SPR_PYLON_NS_E = SPR_ELRAIL_BASE + 38,
 
+	/* sprites for roads */
+	SPR_ROAD_PAVED_STRAIGHT_Y       = 1313,
+	SPR_ROAD_PAVED_STRAIGHT_X       = 1314,
+
 	/* sprites for airports and airfields*/
 	/* Small airports are AIRFIELD, everything else is AIRPORT */
 	SPR_HELIPORT                    = 2633,
--- a/src/table/station_land.h
+++ b/src/table/station_land.h
@@ -959,6 +959,34 @@
 	TILE_SEQ_END()
 };
 
+// drive-through truck stop X
+static const DrawTileSeqStruct _station_display_datas_0168[] = {
+	{  1,  0,  0,  14,  3, 10, SPR_TRUCK_STOP_DT_X_W | (1 << PALETTE_MODIFIER_COLOR), PAL_NONE },
+	{  1, 13,  0,  14,  1, 10, SPR_TRUCK_STOP_DT_X_E | (1 << PALETTE_MODIFIER_COLOR), PAL_NONE },
+	TILE_SEQ_END()
+};
+
+// drive-through truck stop Y
+static const DrawTileSeqStruct _station_display_datas_0169[] = {
+	{ 13,  1,  0,  1, 14, 10, SPR_TRUCK_STOP_DT_Y_W | (1 << PALETTE_MODIFIER_COLOR), PAL_NONE },
+	{  0,  1,  0,  3, 14, 10, SPR_TRUCK_STOP_DT_Y_E | (1 << PALETTE_MODIFIER_COLOR), PAL_NONE },
+	TILE_SEQ_END()
+};
+
+// drive-through bus stop X
+static const DrawTileSeqStruct _station_display_datas_0170[] = {
+	{  5,  0,  0,  8,  3, 10, SPR_BUS_STOP_DT_X_W | (1 << PALETTE_MODIFIER_COLOR), PAL_NONE },
+	{  5, 14,  0,  8,  1, 10, SPR_BUS_STOP_DT_X_E | (1 << PALETTE_MODIFIER_COLOR), PAL_NONE },
+	TILE_SEQ_END()
+};
+
+// drive-through bus stop Y
+static const DrawTileSeqStruct _station_display_datas_0171[] = {
+	{ 13,  5,  0,  1,  8, 10, SPR_BUS_STOP_DT_Y_W | (1 << PALETTE_MODIFIER_COLOR), PAL_NONE },
+	{  0,  5,  0,  3,  8, 10, SPR_BUS_STOP_DT_Y_E | (1 << PALETTE_MODIFIER_COLOR), PAL_NONE },
+	TILE_SEQ_END()
+};
+
 static const DrawTileSprites _station_display_datas[] = {
 	{ SPR_RAIL_TRACK_X,               PAL_NONE, _station_display_datas_0 },
 	{ SPR_RAIL_TRACK_Y,               PAL_NONE, _station_display_datas_1 },
@@ -1128,4 +1156,8 @@
 	{ SPR_FLAT_GRASS_TILE,            PAL_NONE, _station_display_datas_59 },
 	{ SPR_FLAT_GRASS_TILE,            PAL_NONE, _station_display_datas_60 },
 	{ SPR_FLAT_GRASS_TILE,            PAL_NONE, _station_display_datas_61 },
+	{ SPR_ROAD_PAVED_STRAIGHT_X,      PAL_NONE, _station_display_datas_0168 },
+	{ SPR_ROAD_PAVED_STRAIGHT_Y,      PAL_NONE, _station_display_datas_0169 },
+	{ SPR_ROAD_PAVED_STRAIGHT_X,      PAL_NONE, _station_display_datas_0170 },
+	{ SPR_ROAD_PAVED_STRAIGHT_Y,      PAL_NONE, _station_display_datas_0171 }
 };
--- a/src/variables.h
+++ b/src/variables.h
@@ -166,6 +166,7 @@
 	bool autosave_on_exit;              // save an autosave when you quit the game, but do not ask "Do you really want to quit?"
 	byte max_num_autosaves;             // controls how many autosavegames are made before the game starts to overwrite (names them 0 to max_num_autosaves - 1)
 	bool extra_dynamite;                // extra dynamite
+	bool road_stop_on_town_road;        // allow building of drive-through road stops on town owned roads
 
 	bool never_expire_vehicles;         // never expire vehicles
 	byte extend_vehicle_life;           // extend vehicle life by this many years
@@ -211,6 +212,7 @@
 	uint32 npf_water_curve_penalty;        /* The penalty for curves */
 	uint32 npf_road_curve_penalty;         /* The penalty for curves */
 	uint32 npf_crossing_penalty;           /* The penalty for level crossings */
+	uint32 npf_road_drive_through_penalty; /* The penalty for going through a drive-through road stop */
 
 	bool population_in_label; // Show the population of a town in his label?
 
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -2756,9 +2756,11 @@
 			if (IsRoadVehInDepot(v)) /* We'll assume the road vehicle is facing outwards */
 				return DiagdirToDiagTrackdir(GetRoadDepotDirection(v->tile));
 
-			if (IsRoadStopTile(v->tile)) /* We'll assume the road vehicle is facing outwards */
+			if (IsStandardRoadStopTile(v->tile)) /* We'll assume the road vehicle is facing outwards */
 				return DiagdirToDiagTrackdir(GetRoadStopDir(v->tile)); /* Road vehicle in a station */
 
+			if (IsDriveThroughStopTile(v->tile)) return DiagdirToDiagTrackdir(DirToDiagDir(v->direction));
+
 			/* If vehicle's state is a valid track direction (vehicle is not turning around) return it */
 			if (!IsReversingRoadTrackdir((Trackdir)v->u.road.state)) return (Trackdir)v->u.road.state;
 
--- a/src/vehicle.h
+++ b/src/vehicle.h
@@ -45,14 +45,18 @@
 
 	/* Bit numbers */
 	RVS_USING_SECOND_BAY         =    1,                      ///< Only used while in a road stop
+	RVS_IS_STOPPING              =    2,                      ///< Only used for drive-through stops. Vehicle will stop here
 	RVS_DRIVE_SIDE               =    4,                      ///< Only used when retrieving move data and for turning vehicles
 	RVS_IN_ROAD_STOP             =    5,                      ///< The vehicle is in a road stop
+	RVS_IN_DT_ROAD_STOP          =    6,                      ///< The vehicle is in a drive-through road stop
 
 	/* Bit sets of the above specified bits */
 	RVSB_USING_SECOND_BAY        = 1 << RVS_USING_SECOND_BAY, ///< Only used while in a road stop
 	RVSB_DRIVE_SIDE              = 1 << RVS_DRIVE_SIDE,       ///< Only used when retrieving move data and for turning vehicles
 	RVSB_IN_ROAD_STOP            = 1 << RVS_IN_ROAD_STOP,     ///< The vehicle is in a road stop
 	RVSB_IN_ROAD_STOP_END        = RVSB_IN_ROAD_STOP + TRACKDIR_END,
+	RVSB_IN_DT_ROAD_STOP         = 1 << RVS_IN_DT_ROAD_STOP,  ///< The vehicle is in a drive-through road stop
+	RVSB_IN_DT_ROAD_STOP_END     = RVSB_IN_DT_ROAD_STOP + TRACKDIR_END,
 
 	RVSB_TRACKDIR_MASK           = 0x0F,                      ///< The mask used to extract track dirs
 	RVSB_ROAD_STOP_TRACKDIR_MASK = 0x09                       ///< Only bits 0 and 3 are used to encode the trackdir for road stops
--- a/src/yapf/follow_track.hpp
+++ b/src/yapf/follow_track.hpp
@@ -121,8 +121,8 @@
 	/** return true if we can leave m_old_tile in m_exitdir */
 	FORCEINLINE bool CanExitOldTile()
 	{
-		// road stop can be left at one direction only
-		if (IsRoadTT() && IsRoadStopTile(m_old_tile)) {
+		// road stop can be left at one direction only unless it's a drive-through stop
+		if (IsRoadTT() && IsStandardRoadStopTile(m_old_tile)) {
 			DiagDirection exitdir = GetRoadStopDir(m_old_tile);
 			if (exitdir != m_exitdir)
 				return false;
@@ -140,8 +140,8 @@
 	/** return true if we can enter m_new_tile from m_exitdir */
 	FORCEINLINE bool CanEnterNewTile()
 	{
-		if (IsRoadTT() && IsRoadStopTile(m_new_tile)) {
-			// road stop can be entered from one direction only
+		if (IsRoadTT() && IsStandardRoadStopTile(m_new_tile)) {
+			// road stop can be entered from one direction only unless it's a drive-through stop
 			DiagDirection exitdir = GetRoadStopDir(m_new_tile);
 			if (ReverseDiagDir(exitdir) != m_exitdir)
 				return false;
--- a/src/yapf/yapf_road.cpp
+++ b/src/yapf/yapf_road.cpp
@@ -51,6 +51,10 @@
 					if (IsLevelCrossing(tile))
 						cost += Yapf().PfGetSettings().road_crossing_penalty;
 					break;
+				case MP_STATION:
+					if (IsDriveThroughStopTile(tile))
+						cost += Yapf().PfGetSettings().road_stop_penalty;
+					break;
 
 				default:
 					break;
@@ -76,6 +80,10 @@
 			// base tile cost depending on distance between edges
 			segment_cost += Yapf().OneTileCost(tile, trackdir);
 
+			const Vehicle* v = Yapf().GetVehicle();
+			// we have reached the vehicle's destination - segment should end here to avoid target skipping
+			if (v->current_order.type == OT_GOTO_STATION && tile == v->dest_tile) break;
+
 			// stop if we have just entered the depot
 			if (IsTileDepotType(tile, TRANSPORT_ROAD) && trackdir == DiagdirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) {
 				// next time we will reverse and leave the depot
@@ -103,7 +111,6 @@
 			// add min/max speed penalties
 			int min_speed = 0;
 			int max_speed = F.GetSpeedLimit(&min_speed);
-			const Vehicle* v = Yapf().GetVehicle();
 			if (max_speed < v->max_speed) segment_cost += 1 * (v->max_speed - max_speed);
 			if (min_speed > v->max_speed) segment_cost += 10 * (min_speed - v->max_speed);
 
--- a/src/yapf/yapf_settings.h
+++ b/src/yapf/yapf_settings.h
@@ -39,6 +39,7 @@
 	YS_DEF(uint32, road_slope_penalty)         ///< penalty for up-hill slope
 	YS_DEF(uint32, road_curve_penalty)         ///< penalty for curves
 	YS_DEF(uint32, road_crossing_penalty)      ///< penalty for level crossing
+	YS_DEF(uint32, road_stop_penalty)          ///< penalty for going through a drive-through road stop
 	YS_DEF(bool  , rail_firstred_twoway_eol)   ///< treat first red two-way signal as dead end
 	YS_DEF(uint32, rail_firstred_penalty)      ///< penalty for first red signal
 	YS_DEF(uint32, rail_firstred_exit_penalty) ///< penalty for first red exit signal