diff --git a/bin/data/roadstops.grf b/bin/data/roadstops.grf new file mode 100644 index 0000000000..1a243ef6e3 Binary files /dev/null and b/bin/data/roadstops.grf differ diff --git a/src/command.cpp b/src/command.cpp index 942da930a0..bb2955dbbf 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 d46c771dd1..5f6c17e536 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 66b772bb18..8efb62e4fe 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 124b397dcd..bfe43f177e 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 fa565dc6ef..bb864a1853 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 9cba98aa99..f8ac93bb5b 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 056d961558..c5e88e5413 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 2d539f8c9c..535fcbb93b 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<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 ccbf7b6146..704d7de3ea 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 9d76fc4f7b..4af0a269b4 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 4335b2da45..bbc2f66874 100644 --- a/src/saveload.cpp +++ b/src/saveload.cpp @@ -30,7 +30,7 @@ #include "variables.h" #include -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 a321dff1c0..0e9c73a76f 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 cefff603d0..9e649594da 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 ec8be930a0..fc75d5e978 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 @@ -519,6 +526,16 @@ uint RoadStop::AllocateBay() return bay_nr; } +/** + * 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 6d5113b2fd..6d89280aae 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 8698a8bbbb..293caa95f5 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 1128a39f99..49f7591180 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 3a7f365e1c..157272c8db 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 e356ce71b0..1bf72f21aa 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 89d45dcdde..ccc1fc92c4 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 68a2c02544..5197e6fc45 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 aa92a3bec7..93cb131177 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 04ffecf5da..afae9b7882 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 02b306b314..43d98716d8 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 193714dd12..f66b4b7327 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