summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/data/roadstops.grfbin0 -> 2403 bytes
-rw-r--r--src/command.cpp3
-rw-r--r--src/command.h1
-rw-r--r--src/gfxinit.cpp3
-rw-r--r--src/lang/english.txt3
-rw-r--r--src/lang/hungarian.txt3
-rw-r--r--src/npf.cpp14
-rw-r--r--src/pathfind.cpp2
-rw-r--r--src/road_gui.cpp75
-rw-r--r--src/road_map.cpp3
-rw-r--r--src/roadveh_cmd.cpp79
-rw-r--r--src/saveload.cpp2
-rw-r--r--src/settings.cpp4
-rw-r--r--src/settings_gui.cpp1
-rw-r--r--src/station.cpp17
-rw-r--r--src/station.h2
-rw-r--r--src/station_cmd.cpp91
-rw-r--r--src/station_map.h55
-rw-r--r--src/table/sprites.h14
-rw-r--r--src/table/station_land.h32
-rw-r--r--src/variables.h2
-rw-r--r--src/vehicle.cpp4
-rw-r--r--src/vehicle.h4
-rw-r--r--src/yapf/follow_track.hpp8
-rw-r--r--src/yapf/yapf_road.cpp9
-rw-r--r--src/yapf/yapf_settings.h1
26 files changed, 372 insertions, 60 deletions
diff --git a/bin/data/roadstops.grf b/bin/data/roadstops.grf
new file mode 100644
index 000000000..1a243ef6e
--- /dev/null
+++ b/bin/data/roadstops.grf
Binary files differ
diff --git a/src/command.cpp b/src/command.cpp
index 942da930a..bb2955dbb 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -45,6 +45,7 @@ DEF_COMMAND(CmdRenameWaypoint);
DEF_COMMAND(CmdRemoveTrainWaypoint);
DEF_COMMAND(CmdBuildRoadStop);
+DEF_COMMAND(CmdRemoveRoadStop);
DEF_COMMAND(CmdBuildLongRoad);
DEF_COMMAND(CmdRemoveLongRoad);
@@ -188,7 +189,7 @@ static const Command _command_proc_table[] = {
{NULL, 0}, /* 19 */
{NULL, 0}, /* 20 */
{CmdBuildRoadStop, 0}, /* 21 */
- {NULL, 0}, /* 22 */
+ {CmdRemoveRoadStop, 0}, /* 22 */
{CmdBuildLongRoad, 0}, /* 23 */
{CmdRemoveLongRoad, 0}, /* 24 */
{CmdBuildRoad, 0}, /* 25 */
diff --git a/src/command.h b/src/command.h
index d46c771dd..5f6c17e53 100644
--- a/src/command.h
+++ b/src/command.h
@@ -27,6 +27,7 @@ enum {
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,
diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp
index 66b772bb1..8efb62e4f 100644
--- a/src/gfxinit.cpp
+++ b/src/gfxinit.cpp
@@ -387,6 +387,9 @@ static void LoadSpriteTables(void)
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();
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 124b397dc..bfe43f177 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1041,6 +1041,7 @@ STR_CONFIG_PATCHES_BRIBE :{LTBLUE}Allow b
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_1815_ROAD_WITH_STREETLIGHTS :Road with stree
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
diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt
index fa565dc6e..bb864a185 100644
--- a/src/lang/hungarian.txt
+++ b/src/lang/hungarian.txt
@@ -1107,6 +1107,7 @@ STR_CONFIG_PATCHES_BRIBE :{LTBLUE}Önkorm
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_1815_ROAD_WITH_STREETLIGHTS :Út lámpákkal
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
diff --git a/src/npf.cpp b/src/npf.cpp
index 9cba98aa9..f8ac93bb5 100644
--- a/src/npf.cpp
+++ b/src/npf.cpp
@@ -279,6 +279,12 @@ static int32 NPFRoadPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare
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 @@ static bool VehicleMayEnterTile(Owner owner, TileIndex tile, DiagDirection enter
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 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current)
} 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 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current)
}
/* 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)) {
diff --git a/src/pathfind.cpp b/src/pathfind.cpp
index 056d96155..c5e88e541 100644
--- a/src/pathfind.cpp
+++ b/src/pathfind.cpp
@@ -296,7 +296,7 @@ static void TPFMode1(TrackPathFinder* tpf, TileIndex tile, DiagDirection directi
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;
}
diff --git a/src/road_gui.cpp b/src/road_gui.cpp
index 2d539f8c9..535fcbb93 100644
--- 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 @@ void CcRoadDepot(bool success, TileIndex tile, uint32 p1, uint32 p2)
if (success) {
SndPlayTileFx(SND_1F_SPLAT, tile);
ResetObjectToPlace();
- BuildRoadOutsideStation(tile, (DiagDirection)p1);
+ if (!HASBIT(p2, 1)) BuildRoadOutsideStation(tile, (DiagDirection)p1);
}
}
@@ -92,14 +93,45 @@ static void PlaceRoad_Depot(TileIndex tile)
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 @@ static void BuildRoadToolbWndProc(Window *w, WindowEvent *e)
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 @@ static void RoadStationPickerWndProc(Window *w, WindowEvent *e)
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 @@ static void RoadStationPickerWndProc(Window *w, WindowEvent *e)
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 @@ static void RoadStationPickerWndProc(Window *w, WindowEvent *e)
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 void RoadStationPickerWndProc(Window *w, WindowEvent *e)
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 Widget _bus_station_picker_widgets[] = {
};
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 void ShowBusStationPicker(void)
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 Widget _truck_station_picker_widgets[] = {
};
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,
diff --git a/src/road_map.cpp b/src/road_map.cpp
index ccbf7b614..704d7de3e 100644
--- a/src/road_map.cpp
+++ b/src/road_map.cpp
@@ -24,6 +24,7 @@ RoadBits GetAnyRoadBits(TileIndex tile)
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 @@ TrackBits GetAnyRoadTrackBits(TileIndex tile)
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;
}
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index 9d76fc4f7..4af0a269b 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -1080,7 +1080,8 @@ static Trackdir RoadFindPathToDest(Vehicle* v, TileIndex tile, DiagDirection ent
/* 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 @@ static Trackdir RoadFindPathToDest(Vehicle* v, TileIndex tile, DiagDirection ent
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 @@ static Trackdir RoadFindPathToDest(Vehicle* v, TileIndex tile, DiagDirection ent
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 @@ enum {
/* 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 @@ static void RoadVehController(Vehicle *v)
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 @@ again:
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 @@ again:
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 @@ again:
}
}
- 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 @@ again:
/* 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 @@ again:
ClearSlot(v);
}
- rs->SetEntranceBusy(true);
+ if (IsStandardRoadStopTile(v->tile)) rs->SetEntranceBusy(true);
if (rs == v->u.road.slot) {
/* We are leaving the correct station */
diff --git a/src/saveload.cpp b/src/saveload.cpp
index 4335b2da4..bbc2f6687 100644
--- 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!
diff --git a/src/settings.cpp b/src/settings.cpp
index a321dff1c..0e9c73a76 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1340,6 +1340,7 @@ const SettingDesc _patch_settings[] = {
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 @@ const SettingDesc _patch_settings[] = {
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 @@ const SettingDesc _patch_settings[] = {
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 */
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index cefff603d..9e649594d 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -599,6 +599,7 @@ static const char *_patches_stations[] = {
"serviceathelipad",
"modified_catchment",
"gradual_loading",
+ "road_stop_on_town_road",
};
static const char *_patches_economy[] = {
diff --git a/src/station.cpp b/src/station.cpp
index ec8be930a..fc75d5e97 100644
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -502,6 +502,13 @@ bool RoadStop::HasFreeBay() const
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 @@ uint RoadStop::AllocateBay()
}
/**
+ * 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
*/
diff --git a/src/station.h b/src/station.h
index 6d5113b2f..6d89280aa 100644
--- a/src/station.h
+++ b/src/station.h
@@ -66,7 +66,9 @@ struct RoadStop {
/* 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);
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 8698a8bbb..293caa95f 100644
--- 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 @@ static RoadStop **FindRoadStopSpot(bool truck_station, Station* st)
/** 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 @@ int32 CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ static int32 RemoveRoadStop(Station *st, uint32 flags, TileIndex tile)
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 @@ static uint32 VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
/* 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 @@ static int32 ClearTile_Station(TileIndex tile, byte flags)
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;
diff --git a/src/station_map.h b/src/station_map.h
index 1128a39f9..49f759118 100644
--- a/src/station_map.h
+++ b/src/station_map.h
@@ -42,7 +42,9 @@ enum {
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 @@ enum {
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 IsAirport(TileIndex t)
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 @@ static inline bool IsRoadStopTile(TileIndex t)
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 @@ static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a,
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)
diff --git a/src/table/sprites.h b/src/table/sprites.h
index 3a7f365e1..157272c8d 100644
--- a/src/table/sprites.h
+++ b/src/table/sprites.h
@@ -117,6 +117,16 @@ enum Sprites {
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 @@ enum Sprites {
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,
diff --git a/src/table/station_land.h b/src/table/station_land.h
index e356ce71b..1bf72f21a 100644
--- a/src/table/station_land.h
+++ b/src/table/station_land.h
@@ -959,6 +959,34 @@ static const DrawTileSeqStruct _station_display_datas_0163[] = {
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 @@ static const DrawTileSprites _station_display_datas[] = {
{ 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 }
};
diff --git a/src/variables.h b/src/variables.h
index 89d45dcdd..ccc1fc92c 100644
--- a/src/variables.h
+++ b/src/variables.h
@@ -166,6 +166,7 @@ typedef struct Patches {
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 @@ typedef struct Patches {
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?
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 68a2c0254..5197e6fc4 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -2756,9 +2756,11 @@ Trackdir GetVehicleTrackdir(const Vehicle* v)
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;
diff --git a/src/vehicle.h b/src/vehicle.h
index aa92a3bec..93cb13117 100644
--- a/src/vehicle.h
+++ b/src/vehicle.h
@@ -45,14 +45,18 @@ enum RoadVehicleStates {
/* 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
diff --git a/src/yapf/follow_track.hpp b/src/yapf/follow_track.hpp
index 04ffecf5d..afae9b788 100644
--- a/src/yapf/follow_track.hpp
+++ b/src/yapf/follow_track.hpp
@@ -121,8 +121,8 @@ protected:
/** 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 @@ protected:
/** 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;
diff --git a/src/yapf/yapf_road.cpp b/src/yapf/yapf_road.cpp
index 02b306b31..43d98716d 100644
--- a/src/yapf/yapf_road.cpp
+++ b/src/yapf/yapf_road.cpp
@@ -51,6 +51,10 @@ protected:
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 @@ public:
// 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 @@ public:
// 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);
diff --git a/src/yapf/yapf_settings.h b/src/yapf/yapf_settings.h
index 193714dd1..f66b4b732 100644
--- a/src/yapf/yapf_settings.h
+++ b/src/yapf/yapf_settings.h
@@ -39,6 +39,7 @@ YS_DEF_BEGIN
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