Initial implementation of road waypoints

pull/363/head
Jonathan G Rennison 2 years ago
parent ace75f6864
commit b4fc0b4dc5

@ -1032,7 +1032,7 @@
</tr>
</table>
</li>
<li>m6 bits <span style="color: blue">6</span>..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint)</li>
<li>m6 bits <span style="color: blue">6</span>..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint, <span style="color: blue">road waypoint</span>)</li>
<li>m6 bit 2: pbs reservation state for railway stations/waypoints</li>
<li>m7 bits 4..0: <a href="#OwnershipInfo">owner</a> of road (road stops)</li>

@ -67,6 +67,7 @@ CommandProc CmdBuildTunnel;
CommandProc CmdBuildTrainDepot;
CommandProcEx CmdBuildRailWaypoint;
CommandProc CmdBuildRoadWaypoint;
CommandProc CmdRenameWaypoint;
CommandProc CmdSetWaypointLabelHidden;
CommandProc CmdRemoveFromRailWaypoint;
@ -316,6 +317,7 @@ static const Command _command_proc_table[] = {
DEF_CMD(CmdRemoveFromRailStation, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_FROM_RAIL_STATION
DEF_CMD(CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CONVERT_RAIL
DEF_CMD(CmdBuildRailWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_RAIL_WAYPOINT
DEF_CMD(CmdBuildRoadWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD_WAYPOINT
DEF_CMD(CmdRenameWaypoint, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_WAYPOINT
DEF_CMD(CmdSetWaypointLabelHidden, 0, CMDT_OTHER_MANAGEMENT ), // CMD_SET_WAYPOINT_LABEL_HIDDEN
DEF_CMD(CmdRemoveFromRailWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_FROM_RAIL_WAYPOINT

@ -251,6 +251,7 @@ enum Commands {
CMD_CONVERT_RAIL, ///< convert a rail type
CMD_BUILD_RAIL_WAYPOINT, ///< build a waypoint
CMD_BUILD_ROAD_WAYPOINT, ///< build a road waypoint
CMD_RENAME_WAYPOINT, ///< rename a waypoint
CMD_SET_WAYPOINT_LABEL_HIDDEN, ///< set whether waypoint label is hidden
CMD_REMOVE_FROM_RAIL_WAYPOINT, ///< remove a (rectangle of) tiles from a rail waypoint

@ -3549,6 +3549,8 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}Build ro
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}Build tramway section using the Autotram mode. Ctrl toggles build/remove for tramway construction. Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build road vehicle depot (for buying and servicing vehicles). Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build tram vehicle depot (for buying and servicing vehicles). Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT :{BLACK}Convert road to waypoint. Ctrl enables joining waypoints. Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT :{BLACK}Convert tram to waypoint. Ctrl enables joining waypoints. Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}Build bus station. Ctrl enables joining stations. Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION :{BLACK}Build passenger tram station. Ctrl enables joining stations. Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY :{BLACK}Build lorry loading bay. Ctrl enables joining stations. Shift toggles building/showing cost estimate
@ -6142,11 +6144,14 @@ STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Adjoins
STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT :{WHITE}Too close to another waypoint
STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT :{WHITE}Can't build train waypoint here...
STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT :{WHITE}Can't build road waypoint here...
STR_ERROR_CAN_T_POSITION_BUOY_HERE :{WHITE}Can't place buoy here...
STR_ERROR_CAN_T_CHANGE_WAYPOINT_NAME :{WHITE}Can't change waypoint name...
STR_ERROR_CAN_T_REMOVE_TRAIN_WAYPOINT :{WHITE}Can't remove train waypoint here...
STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT :{WHITE}Can't remove road waypoint here...
STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST :{WHITE}Must remove rail waypoint first
STR_ERROR_MUST_REMOVE_ROADWAYPOINT_FIRST :{WHITE}Must remove road waypoint first
STR_ERROR_BUOY_IN_THE_WAY :{WHITE}... buoy in the way
STR_ERROR_BUOY_IS_IN_USE :{WHITE}... buoy is in use by another company!

@ -1090,6 +1090,14 @@ CommandCost CmdInsertOrderIntl(DoCommandFlag flags, Vehicle *v, VehicleOrderID s
break;
}
case VEH_ROAD: {
if (!(wp->facilities & FACIL_BUS_STOP) || !(wp->facilities & FACIL_TRUCK_STOP)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
CommandCost ret = CheckInfraUsageAllowed(v->type, wp->owner);
if (ret.Failed()) return ret;
break;
}
case VEH_SHIP:
if (!(wp->facilities & FACIL_DOCK)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
if (wp->owner != OWNER_NONE) {
@ -1101,9 +1109,9 @@ CommandCost CmdInsertOrderIntl(DoCommandFlag flags, Vehicle *v, VehicleOrderID s
/* Order flags can be any of the following for waypoints:
* [non-stop]
* non-stop orders (if any) are only valid for trains */
if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN) return CMD_ERROR;
if (_settings_game.order.nonstop_only && !(new_order.GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) && v->type == VEH_TRAIN) return CMD_ERROR;
* non-stop orders (if any) are only valid for trains/RVs */
if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR;
if (_settings_game.order.nonstop_only && !(new_order.GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) && v->IsGroundVehicle()) return CMD_ERROR;
break;
}

@ -1130,6 +1130,15 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
return order;
}
/* check road waypoint */
if (IsRoadWaypointTile(tile) &&
v->type == VEH_ROAD &&
IsInfraTileUsageAllowed(VEH_ROAD, v->owner, tile)) {
order.MakeGoToWaypoint(GetStationIndex(tile));
if (_settings_client.gui.new_nonstop != _ctrl_pressed || _settings_game.order.nonstop_only) order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
return order;
}
/* check buoy (no ownership) */
if (IsBuoyTile(tile) && v->type == VEH_SHIP) {
order.MakeGoToWaypoint(GetStationIndex(tile));

@ -228,7 +228,7 @@ protected:
/* special handling for stations */
if (IsRailTT() && HasStationTileRail(m_new_tile)) {
m_is_station = true;
} else if (IsRoadTT() && IsRoadStopTile(m_new_tile)) {
} else if (IsRoadTT() && IsStationRoadStopTile(m_new_tile)) {
m_is_station = true;
}
}

@ -341,6 +341,7 @@ static int32 NPFRoadPathCost(AyStar *as, AyStarNode *current, OpenListNode *pare
case MP_STATION: {
cost = NPF_TILE_LENGTH;
if (IsRoadWaypoint(tile)) break;
const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
if (IsDriveThroughStopTile(tile)) {
/* Increase the cost for drive-through road stops */
@ -1131,7 +1132,7 @@ static void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, const Vehicle *
if (v->type == VEH_TRAIN) {
fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT;
} else if (v->type == VEH_ROAD) {
fstd->station_type = RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK;
fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? (RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK) : STATION_ROADWAYPOINT;
} else if (v->type == VEH_SHIP) {
fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? STATION_DOCK : STATION_BUOY;
}

@ -92,6 +92,8 @@ protected:
break;
case MP_STATION: {
if (IsRoadWaypoint(tile)) break;
const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
if (IsDriveThroughStopTile(tile)) {
/* Increase the cost for drive-through road stops */
@ -270,7 +272,7 @@ protected:
TileIndex m_destTile;
TrackdirBits m_destTrackdirs;
StationID m_dest_station;
bool m_bus;
StationType m_station_type;
bool m_non_artic;
public:
@ -278,8 +280,14 @@ public:
{
if (v->current_order.IsType(OT_GOTO_STATION)) {
m_dest_station = v->current_order.GetDestination();
m_bus = v->IsBus();
m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_bus ? STATION_BUS : STATION_TRUCK);
m_station_type = v->IsBus() ? STATION_BUS : STATION_TRUCK;
m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_station_type);
m_non_artic = !v->HasArticulatedPart();
m_destTrackdirs = INVALID_TRACKDIR_BIT;
} else if (v->current_order.IsType(OT_GOTO_WAYPOINT)) {
m_dest_station = v->current_order.GetDestination();
m_station_type = STATION_ROADWAYPOINT;
m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_station_type);
m_non_artic = !v->HasArticulatedPart();
m_destTrackdirs = INVALID_TRACKDIR_BIT;
} else {
@ -313,7 +321,7 @@ public:
if (m_dest_station != INVALID_STATION) {
return IsTileType(tile, MP_STATION) &&
GetStationIndex(tile) == m_dest_station &&
(m_bus ? IsBusStop(tile) : IsTruckStop(tile)) &&
(m_station_type == GetStationType(tile)) &&
(m_non_artic || IsDriveThroughStopTile(tile));
}

@ -1967,7 +1967,7 @@ void DrawRoadCatenary(const TileInfo *ti)
tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y);
}
} else if (IsTileType(ti->tile, MP_STATION)) {
if (IsRoadStop(ti->tile)) {
if (IsAnyRoadStop(ti->tile)) {
if (IsDriveThroughStopTile(ti->tile)) {
Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
tram = road = (axis == AXIS_X ? ROAD_X : ROAD_Y);
@ -2976,7 +2976,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
TileType tt = GetTileType(tile);
switch (tt) {
case MP_STATION:
if (!IsRoadStop(tile)) continue;
if (!IsAnyRoadStop(tile)) continue;
break;
case MP_ROAD:
if (IsLevelCrossing(tile) && RoadNoLevelCrossing(to_type)) {
@ -3036,7 +3036,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
uint num_pieces;
if (IsRoadDepotTile(tile) || IsRoadStopTile(tile)) {
if (IsRoadDepotTile(tile) || IsAnyRoadStopTile(tile)) {
num_pieces = HasTileRoadType(tile, rtt) ? 2 : 0;
} else {
num_pieces = CountBits(GetAnyRoadBits(tile, rtt));
@ -3047,7 +3047,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (flags & DC_EXEC) { // we can safely convert, too
/* Update the company infrastructure counters. */
if (!IsRoadStopTile(tile) && owner == _current_company) {
if (!IsAnyRoadStopTile(tile) && owner == _current_company) {
ConvertRoadTypeOwner(tile, num_pieces, owner, from_type, to_type);
} else {
UpdateCompanyRoadInfrastructure(from_type, owner, -(int)num_pieces);

@ -32,6 +32,7 @@
#include "core/geometry_func.hpp"
#include "date_func.h"
#include "station_map.h"
#include "waypoint_func.h"
#include "widgets/road_widget.h"
#include "table/strings.h"
@ -197,6 +198,29 @@ static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, uint32 p2, u
ShowSelectStationIfNeeded(cmdcont, ta);
}
/**
* Place a road waypoint.
* @param tile Position to start dragging a waypoint.
*/
static void PlaceRoad_Waypoint(TileIndex tile)
{
if (_remove_button_clicked) {
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_ROAD_WAYPOINT);
return;
}
Axis axis = GetAxisForNewRoadWaypoint(tile);
if (IsValidAxis(axis)) {
/* Valid tile for waypoints */
VpStartPlaceSizing(tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_ROAD_WAYPOINT);
VpSetPlaceSizingLimit(_settings_game.station.station_spread);
} else {
/* Tile where we can't build rail waypoints. This is always going to fail,
* but provides the user with a proper error message. */
DoCommandP(tile, 1 | 1 << 8, INVALID_STATION << 16, CMD_BUILD_ROAD_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT));
}
}
/**
* Callback for placing a bus station.
* @param tile Position to place the station.
@ -309,6 +333,7 @@ struct BuildRoadToolbarWindow : Window {
bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD, rtt);
this->SetWidgetsDisabledState(!can_build,
WID_ROT_DEPOT,
WID_ROT_BUILD_WAYPOINT,
WID_ROT_BUS_STATION,
WID_ROT_TRUCK_STATION,
WIDGET_LIST_END);
@ -322,10 +347,12 @@ struct BuildRoadToolbarWindow : Window {
if (!can_build) {
/* Show in the tooltip why this button is disabled. */
this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
} else {
this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT);
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT : STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT);
this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION);
this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION);
}
@ -405,6 +432,7 @@ struct BuildRoadToolbarWindow : Window {
case WID_ROT_BUS_STATION:
case WID_ROT_TRUCK_STATION:
case WID_ROT_BUILD_WAYPOINT:
if (RoadTypeIsRoad(this->roadtype)) this->DisableWidget(WID_ROT_ONE_WAY);
this->SetWidgetDisabledState(WID_ROT_REMOVE, !this->IsWidgetLowered(clicked_widget));
break;
@ -465,6 +493,12 @@ struct BuildRoadToolbarWindow : Window {
}
break;
case WID_ROT_BUILD_WAYPOINT:
if (HandlePlacePushButton(this, WID_ROT_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, HT_RECT)) {
this->last_started_action = widget;
}
break;
case WID_ROT_BUS_STATION:
if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT)) {
ShowRVStationPicker(this, ROADSTOP_BUS);
@ -554,6 +588,10 @@ struct BuildRoadToolbarWindow : Window {
CMD_BUILD_ROAD_DEPOT | CMD_MSG(this->rti->strings.err_depot), CcRoadDepot);
break;
case WID_ROT_BUILD_WAYPOINT:
PlaceRoad_Waypoint(tile);
break;
case WID_ROT_BUS_STATION:
PlaceRoad_BusStation(tile);
break;
@ -675,6 +713,22 @@ struct BuildRoadToolbarWindow : Window {
CMD_BUILD_LONG_ROAD | CMD_MSG(this->rti->strings.err_build_road), CcPlaySound_CONSTRUCTION_OTHER);
break;
case DDSP_BUILD_ROAD_WAYPOINT:
case DDSP_REMOVE_ROAD_WAYPOINT:
if (this->IsWidgetLowered(WID_ROT_BUILD_WAYPOINT)) {
TileArea ta(start_tile, end_tile);
if (_remove_button_clicked) {
DoCommandP(ta.tile, ta.w | ta.h << 8, (1 << 2), CMD_REMOVE_ROAD_STOP | CMD_MSG(STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT), CcPlaySound_CONSTRUCTION_OTHER);
} else {
uint32 p1 = ta.w | ta.h << 8 | _ctrl_pressed << 16 | (select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y) << 17;
uint32 p2 = INVALID_STATION << 16;
CommandContainer cmdcont = NewCommandContainerBasic(ta.tile, p1, p2, CMD_BUILD_ROAD_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT), CcPlaySound_CONSTRUCTION_OTHER);
ShowSelectWaypointIfNeeded(cmdcont, ta);
}
}
break;
case DDSP_BUILD_BUSSTOP:
case DDSP_REMOVE_BUSSTOP:
if (this->IsWidgetLowered(WID_ROT_BUS_STATION)) {
@ -812,6 +866,8 @@ static const NWidgetPart _nested_build_road_widgets[] = {
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION),
@ -855,6 +911,8 @@ static const NWidgetPart _nested_build_tramway_widgets[] = {
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION),
@ -1314,7 +1372,7 @@ static void SetDefaultRoadGui()
std::array<uint, ROADTYPE_END> tram_count = {};
for (TileIndex t = 0; t < MapSize(); t++) {
if (MayHaveRoad(t)) {
if (IsTileType(t, MP_STATION) && !IsRoadStop(t)) continue;
if (IsTileType(t, MP_STATION) && !IsAnyRoadStop(t)) continue;
RoadType road_type = GetRoadTypeRoad(t);
if (road_type != INVALID_ROADTYPE) road_count[road_type]++;
RoadType tram_type = GetRoadTypeTram(t);

@ -44,7 +44,7 @@ RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_b
}
case MP_STATION:
if (!IsRoadStopTile(tile)) return ROAD_NONE;
if (!IsAnyRoadStopTile(tile)) return ROAD_NONE;
if (IsDriveThroughStopTile(tile)) return (GetRoadStopDir(tile) == DIAGDIR_NE) ? ROAD_X : ROAD_Y;
return DiagDirToRoadBits(GetRoadStopDir(tile));

@ -1804,7 +1804,7 @@ again:
v->tile != tile) {
/* So, keep 'our' state */
dir = (Trackdir)v->state;
} else if (IsRoadStop(v->tile)) {
} else if (IsStationRoadStop(v->tile)) {
/* We're not continuing our drive through road stop, so leave. */
RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
}

@ -1234,7 +1234,7 @@ bool AfterLoadGame()
break;
case MP_STATION:
if (IsRoadStop(t)) SB(_me[t].m7, 6, 2, 1);
if (IsStationRoadStop(t)) SB(_me[t].m7, 6, 2, 1);
break;
case MP_TUNNELBRIDGE:
@ -1288,7 +1288,7 @@ bool AfterLoadGame()
break;
case MP_STATION:
if (!IsRoadStop(t)) break;
if (!IsStationRoadStop(t)) break;
if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_m[t].m3, 0, 3));
SB(_me[t].m7, 0, 5, HasBit(_me[t].m6, 2) ? OWNER_TOWN : GetTileOwner(t));
@ -1445,7 +1445,7 @@ bool AfterLoadGame()
has_road = true;
break;
case MP_STATION:
has_road = IsRoadStop(t);
has_road = IsAnyRoadStop(t);
break;
case MP_TUNNELBRIDGE:
has_road = GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD;
@ -1499,7 +1499,7 @@ bool AfterLoadGame()
has_road = true;
break;
case MP_STATION:
has_road = IsRoadStop(t);
has_road = IsAnyRoadStop(t);
break;
case MP_TUNNELBRIDGE:
has_road = GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD;

@ -160,7 +160,8 @@ void AfterLoadCompanyStats()
break;
case STATION_BUS:
case STATION_TRUCK: {
case STATION_TRUCK:
case STATION_ROADWAYPOINT: {
/* Iterate all present road types as each can have a different owner. */
for (RoadTramType rtt : _roadtramtypes) {
RoadType rt = GetRoadType(tile, rtt);

@ -166,6 +166,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_ST_INDUSTRY_CARGO_MODE, XSCF_IGNORABLE_UNKNOWN, 1, 1, "st_industry_cargo_mode", nullptr, nullptr, nullptr },
{ XSLFI_TL_SPEED_LIMIT, XSCF_IGNORABLE_UNKNOWN, 1, 1, "tl_speed_limit", nullptr, nullptr, nullptr },
{ XSLFI_WAYPOINT_FLAGS, XSCF_NULL, 1, 1, "waypoint_flags", nullptr, nullptr, nullptr },
{ XSLFI_ROAD_WAYPOINTS, XSCF_NULL, 1, 1, "road_waypoints", nullptr, nullptr, nullptr },
{ XSLFI_MORE_STATION_TYPES, XSCF_NULL, 1, 1, "more_station_types", nullptr, nullptr, nullptr },
{ XSLFI_SCRIPT_INT64, XSCF_NULL, 1, 1, "script_int64", nullptr, nullptr, nullptr },
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker

@ -120,6 +120,7 @@ enum SlXvFeatureIndex {
XSLFI_ST_INDUSTRY_CARGO_MODE, ///< Station industry cargo mode setting
XSLFI_TL_SPEED_LIMIT, ///< Through load maximum speed setting
XSLFI_WAYPOINT_FLAGS, ///< Waypoint flags
XSLFI_ROAD_WAYPOINTS, ///< Road waypoints
XSLFI_MORE_STATION_TYPES, ///< More station types (field widening)
XSLFI_SCRIPT_INT64, ///< See: SLV_SCRIPT_INT64

@ -462,6 +462,9 @@ static const SaveLoad _waypoint_desc[] = {
SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION),
SLE_CONDVAR_X(Waypoint, waypoint_flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_WAYPOINT_FLAGS)),
SLE_CONDVAR_X(Waypoint, road_waypoint_area.tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)),
SLE_CONDVAR_X(Waypoint, road_waypoint_area.w, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)),
SLE_CONDVAR_X(Waypoint, road_waypoint_area.h, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)),
};
/**

@ -1135,6 +1135,8 @@ static byte GetTopographyValue(TileIndex tile)
return MKCOLOUR(PC_WATER);
case STATION_WAYPOINT:
return MKCOLOUR(PC_GREY);
case STATION_ROADWAYPOINT:
return MKCOLOUR(PC_GREY);
default: NOT_REACHED();
}
}
@ -1228,6 +1230,8 @@ static byte GetIndustryValue(TileIndex tile)
return MKCOLOUR(PC_BLACK);
case STATION_WAYPOINT:
return MKCOLOUR(PC_GREY);
case STATION_ROADWAYPOINT:
return MKCOLOUR(PC_GREY);
default: NOT_REACHED();
}
}

@ -50,7 +50,7 @@
if (!::IsValidTile(tile)) return false;
if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
return ::IsRoadStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
return ::IsStationRoadStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
}
/* static */ bool ScriptRoad::IsDriveThroughRoadStationTile(TileIndex tile)
@ -624,7 +624,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD
EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, IsTileType(tile, MP_STATION));
EnforcePrecondition(false, IsRoadStop(tile));
EnforcePrecondition(false, IsStationRoadStop(tile));
return ScriptObject::DoCommand(tile, 1 | 1 << 8, GetRoadStopType(tile), CMD_REMOVE_ROAD_STOP);
}

@ -314,13 +314,15 @@ static uint GetTileCatchmentRadius(TileIndex tile, const Station *st)
default: NOT_REACHED();
case STATION_BUOY:
case STATION_WAYPOINT: return CA_NONE;
case STATION_WAYPOINT:
case STATION_ROADWAYPOINT: return CA_NONE;
}
} else {
switch (GetStationType(tile)) {
default: return CA_UNMODIFIED + inc;
case STATION_BUOY:
case STATION_WAYPOINT: return CA_NONE;
case STATION_WAYPOINT:
case STATION_ROADWAYPOINT: return CA_NONE;
}
}
}

@ -98,8 +98,8 @@ bool IsHangar(TileIndex t)
* @param st to 'return' the found station
* @return Succeeded command (if zero or one station found) or failed command (for two or more stations found).
*/
template <class T>
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st)
template <class T, class F>
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
{
ta.Expand(1);
@ -107,7 +107,7 @@ CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID c
for (TileIndex tile_cur : ta) {
if (IsTileType(tile_cur, MP_STATION)) {
StationID t = GetStationIndex(tile_cur);
if (!T::IsValidID(t) || Station::Get(t)->owner != company) continue;
if (!T::IsValidID(t) || T::Get(t)->owner != company || !filter(T::Get(t))) continue;
if (closest_station == INVALID_STATION) {
closest_station = t;
} else if (closest_station != t) {
@ -1068,13 +1068,14 @@ static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag fl
* @param flags Operation to perform.
* @param invalid_dirs Prohibited directions (set of DiagDirections).
* @param is_drive_through True if trying to build a drive-through station.
* @param is_truck_stop True when building a truck stop, false otherwise.
* @param station_type Station type (bus, truck or road waypoint).
* @param axis Axis of a drive-through road stop.
* @param station StationID to be queried and returned if available.
* @param rt Road type to build.
* @param require_road Is existing road required.
* @return The cost in case of success, or an error code if it failed.
*/
static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadType rt)
CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt, bool require_road)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
int allowed_z = -1;
@ -1097,10 +1098,10 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags
* Station points to INVALID_STATION if we can build on any station.
* Or it points to a station if we're only allowed to build on exactly that station. */
if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
if (!IsRoadStop(cur_tile)) {
if (!IsAnyRoadStop(cur_tile)) {
return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
} else {
if (is_truck_stop != IsTruckStop(cur_tile) ||
if (station_type != GetStationType(cur_tile) ||
is_drive_through != IsDriveThroughStopTile(cur_tile)) {
return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
}
@ -1147,10 +1148,10 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
if (RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
} else if (RoadTypeIsRoad(rt)) {
} else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
@ -1168,12 +1169,14 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
if (RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
} else if (RoadTypeIsTram(rt)) {
} else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
} else if (require_road) {
return_cmd_error(STR_ERROR_THERE_IS_NO_ROAD);
} else {
ret = DoCommand(cur_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;
@ -1316,8 +1319,8 @@ void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const Station
* @param error_message the error message when building a station on top of others
* @return command cost with the error or 'okay'
*/
template <class T>
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, StringID error_message)
template <class T, class F>
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, StringID error_message, F filter)
{
assert(*st == nullptr);
bool check_surrounding = true;
@ -1331,7 +1334,8 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station
} else {
/* Extend the current station, and don't check whether it will
* be near any other stations. */
*st = T::GetIfValid(existing_station);
T *candidate = T::GetIfValid(existing_station);
if (candidate != nullptr && filter(candidate)) *st = candidate;
check_surrounding = (*st == nullptr);
}
} else {
@ -1343,7 +1347,7 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station
if (check_surrounding) {
/* Make sure there is no more than one other station around us that is owned by us. */
CommandCost ret = GetStationAround(ta, existing_station, _current_company, st);
CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
if (ret.Failed()) return ret;
}
@ -1365,7 +1369,7 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station
*/
static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st, StringID error_message = STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST)
{
return FindJoiningBaseStation<Station>(existing_station, station_to_join, adjacent, ta, st, error_message);
return FindJoiningBaseStation<Station>(existing_station, station_to_join, adjacent, ta, st, error_message, [](Station *st) -> bool { return true; });
}
/**
@ -1377,9 +1381,11 @@ static CommandCost FindJoiningStation(StationID existing_station, StationID stat
* @param wp 'return' pointer for the found waypoint
* @return command cost with the error or 'okay'
*/
CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp)
CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
{
return FindJoiningBaseStation<Waypoint>(existing_waypoint, waypoint_to_join, adjacent, ta, wp, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST);
return FindJoiningBaseStation<Waypoint>(existing_waypoint, waypoint_to_join, adjacent, ta, wp,
is_road ? STR_ERROR_MUST_REMOVE_ROADWAYPOINT_FIRST : STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST,
[is_road](Waypoint *wp) -> bool { return HasBit(wp->waypoint_flags, WPF_ROAD) == is_road; });
}
/**
@ -1736,6 +1742,16 @@ static void MakeShipStationAreaSmaller(Station *st)
UpdateStationDockingTiles(st);
}
static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
{
return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
}
void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
{
road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
}
/**
* Remove a number of tiles from any rail station within the area.
* @param ta the area to clear station tile from.
@ -2004,7 +2020,7 @@ static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
}
}
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags);
CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags);
/**
* Find a nearby station that joins this road stop.
@ -2017,7 +2033,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags);
*/
static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
{
return FindJoiningBaseStation<Station>(existing_stop, station_to_join, adjacent, ta, st, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST);
return FindJoiningBaseStation<Station>(existing_stop, station_to_join, adjacent, ta, st, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST, [](Station *st) -> bool { return true; });
}
/**
@ -2082,7 +2098,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
/* Total road stop cost. */
CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[type ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]);
StationID est = INVALID_STATION;
ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, type, axis, &est, rt);
ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, type ? STATION_TRUCK : STATION_BUS, axis, &est, rt, false);
if (ret.Failed()) return ret;
cost.AddCost(ret);
@ -2106,7 +2122,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
DisallowedRoadDirections drd = IsNormalRoadTile(cur_tile) ? GetDisallowedRoadDirections(cur_tile) : DRD_NONE;
if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) {
if (IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile)) {
RemoveRoadStop(cur_tile, flags);
}
@ -2141,7 +2157,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
UpdateCompanyRoadInfrastructure(road_rt, road_owner, ROAD_STOP_TRACKBIT_FACTOR);
UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, ROAD_STOP_TRACKBIT_FACTOR);
MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, road_rt, tram_rt, axis);
MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), road_rt, tram_rt, axis);
SetDriveThroughStopDisallowedRoadDirections(cur_tile, drd);
road_stop->MakeDriveThrough();
} else {
@ -2154,10 +2170,10 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
Company::Get(st->owner)->infrastructure.station++;
MarkTileDirtyByTile(cur_tile);
UpdateRoadCachedOneWayStatesAroundTile(cur_tile);
}
ZoningMarkDirtyStationCoverageArea(st);
NotifyRoadLayoutChanged(true);
UpdateRoadCachedOneWayStatesAroundTile(tile);
if (st != nullptr) {
st->AfterStationTileSetChange(true, type ? STATION_TRUCK: STATION_BUS);
@ -2181,6 +2197,52 @@ static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
return nullptr;
}
CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags)
{
Waypoint *wp = Waypoint::GetByTile(tile);
if (_current_company != OWNER_WATER) {
CommandCost ret = CheckOwnership(wp->owner);
if (ret.Failed()) return ret;
}
/* don't do the check for drive-through road stops when company bankrupts */
if (!(flags & DC_BANKRUPT)) {
CommandCost ret = EnsureNoVehicleOnGround(tile);
if (ret.Failed()) return ret;
}
if (flags & DC_EXEC) {
/* Update company infrastructure counts. */
for (RoadTramType rtt : _roadtramtypes) {
RoadType rt = GetRoadType(tile, rtt);
UpdateCompanyRoadInfrastructure(rt, GetRoadOwner(tile, rtt), -static_cast<int>(ROAD_STOP_TRACKBIT_FACTOR));
}
Company::Get(wp->owner)->infrastructure.station--;
DirtyCompanyInfrastructureWindows(wp->owner);
DoClearSquare(tile);
wp->rect.AfterRemoveTile(wp, tile);
MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
UpdateStationSignCoord(wp);
/* if we deleted the whole waypoint, delete the road facility. */
if (wp->road_waypoint_area.tile == INVALID_TILE) {
wp->facilities &= ~(FACIL_BUS_STOP | FACIL_TRUCK_STOP);
SetWindowWidgetDirty(WC_STATION_VIEW, wp->index, WID_SV_ROADVEHS);
wp->UpdateVirtCoord();
DeleteStationIfEmpty(wp);
}
NotifyRoadLayoutChanged(false);
}
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_TRUCK]);
}
/**
* Remove a bus station/truck stop
@ -2188,8 +2250,12 @@ static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
* @param flags operation to perform
* @return cost or failure of operation
*/
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
{
if (IsRoadWaypoint(tile)) {
return RemoveRoadWaypointStop(tile, flags);
}
Station *st = Station::GetByTile(tile);
if (_current_company != OWNER_WATER) {
@ -2289,6 +2355,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
* bit 8..15: Height of the removal area.
* @param p2 bit 0: 0 For bus stops, 1 for truck stops.
* @param p2 bit 1: 0 to keep roads of all drive-through stops, 1 to remove them.
* @param p2 bit 2: 0 for bus/truck stops, 1 for road waypoints.
* @param text Unused.
* @return The cost of this operation or an error.
*/
@ -2296,7 +2363,7 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
{
uint8 width = (uint8)GB(p1, 0, 8);
uint8 height = (uint8)GB(p1, 8, 8);
bool keep_drive_through_roads = !HasBit(p2, 1);
bool keep_drive_through_roads = !HasBit(p2, 1) || HasBit(p2, 2);
/* Check for incorrect width / height. */
if (width == 0 || height == 0) return CMD_ERROR;
@ -2312,8 +2379,13 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
bool had_success = false;
for (TileIndex cur_tile : roadstop_area) {
/* Make sure the specified tile is a road stop of the correct type */
if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || (uint32)GetRoadStopType(cur_tile) != GB(p2, 0, 1)) continue;
if (HasBit(p2, 2)) {
/* Make sure the specified tile is a road waypoint */
if (!IsTileType(cur_tile, MP_STATION) || !IsRoadWaypoint(cur_tile)) continue;
} else {
/* Make sure the specified tile is a road stop of the correct type */
if (!IsTileType(cur_tile, MP_STATION) || !IsStationRoadStop(cur_tile) || (uint32)GetRoadStopType(cur_tile) != GB(p2, 0, 1)) continue;
}
/* Save information on to-be-restored roads before the stop is removed. */
RoadBits road_bits = ROAD_NONE;
@ -3336,7 +3408,7 @@ draw_default_foundation:
if (HasStationRail(ti->tile) && HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
if (IsRoadStop(ti->tile)) {
if (IsAnyRoadStop(ti->tile)) {
RoadType road_rt = GetRoadTypeRoad(ti->tile);
RoadType tram_rt = GetRoadTypeTram(ti->tile);
const RoadTypeInfo* road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
@ -3441,7 +3513,7 @@ static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
{
td->owner[0] = GetTileOwner(tile);
if (IsRoadStopTile(tile)) {
if (IsAnyRoadStopTile(tile)) {
RoadType road_rt = GetRoadTypeRoad(tile);
RoadType tram_rt = GetRoadTypeTram(tile);
Owner road_owner = INVALID_OWNER;
@ -3535,6 +3607,7 @@ static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
case STATION_DOCK: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
case STATION_BUOY: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
case STATION_WAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
case STATION_ROADWAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
}
td->str = str;
}
@ -3564,7 +3637,7 @@ static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode
break;
case TRANSPORT_ROAD:
if (IsRoadStop(tile)) {
if (IsAnyRoadStop(tile)) {
RoadTramType rtt = (RoadTramType)sub_mode;
if (!HasTileRoadType(tile, rtt)) break;
@ -3733,7 +3806,7 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i
} else if (v->type == VEH_ROAD) {
RoadVehicle *rv = RoadVehicle::From(v);
if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
if (IsRoadStop(tile) && rv->IsFrontEngine()) {
if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
/* Attempt to allocate a parking bay in a road stop */
return RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv) ? VETSB_CONTINUE : VETSB_CANNOT_ENTER;
}
@ -4712,7 +4785,7 @@ void DeleteOilRig(TileIndex tile)
static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
{
if (IsRoadStopTile(tile)) {
if (IsAnyRoadStopTile(tile)) {
for (RoadTramType rtt : _roadtramtypes) {
/* Update all roadtypes, no matter if they are present */
if (GetRoadOwner(tile, rtt) == old_owner) {
@ -4749,6 +4822,7 @@ static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_o
case STATION_BUS:
case STATION_TRUCK:
case STATION_ROADWAYPOINT:
/* Road stops were already handled above. */
break;
@ -4839,6 +4913,7 @@ CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
default: break;
case STATION_RAIL: return_cmd_error(STR_ERROR_MUST_DEMOLISH_RAILROAD);
case STATION_WAYPOINT: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
case STATION_ROADWAYPOINT: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
case STATION_AIRPORT: return_cmd_error(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
case STATION_BUS: return_cmd_error(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
@ -4866,6 +4941,11 @@ CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
return RemoveRoadStopAndUpdateRoadCachedOneWayState(tile, flags);
case STATION_BUOY: return RemoveBuoy(tile, flags);
case STATION_DOCK: return RemoveDock(tile, flags);
case STATION_ROADWAYPOINT:
if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags)) {
return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
}
return RemoveRoadStopAndUpdateRoadCachedOneWayState(tile, flags);
default: break;
}
@ -4892,7 +4972,8 @@ static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, in
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
case STATION_TRUCK:
case STATION_BUS: {
case STATION_BUS:
case STATION_ROADWAYPOINT: {
DiagDirection direction = GetRoadStopDir(tile);
if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
if (IsDriveThroughStopTile(tile)) {

@ -2326,6 +2326,10 @@ struct TileAndStation {
static std::vector<TileAndStation> _deleted_stations_nearby;
static std::vector<StationID> _stations_nearby_list;
static bool _station_nearby_road_waypoint_search;
static bool IsNearbyStationRightType(const Station *st) { return true; }
static bool IsNearbyStationRightType(const Waypoint *wp) { return HasBit(wp->waypoint_flags, WPF_ROAD) == _station_nearby_road_waypoint_search; }
/**
* Add station on this tile to _stations_nearby_list if it's fully within the
@ -2358,7 +2362,7 @@ static bool AddNearbyStation(TileIndex tile, void *user_data)
if (!T::IsValidID(sid)) return false;
T *st = T::Get(sid);
if (st->owner != _local_company || std::find(_stations_nearby_list.begin(), _stations_nearby_list.end(), sid) != _stations_nearby_list.end()) return false;
if (st->owner != _local_company || !IsNearbyStationRightType(st) || std::find(_stations_nearby_list.begin(), _stations_nearby_list.end(), sid) != _stations_nearby_list.end()) return false;
if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST).Succeeded()) {
_stations_nearby_list.push_back(sid);
@ -2391,7 +2395,7 @@ static const T *FindStationsNearby(TileArea ta, bool distant_join)
/* Look for deleted stations */
for (const BaseStation *st : BaseStation::Iterate()) {
if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company && IsNearbyStationRightType(T::From(st))) {
/* Include only within station spread (yes, it is strictly less than) */
if (std::max(DistanceMax(ta.tile, st->xy), DistanceMax(TILE_ADDXY(ta.tile, ta.w - 1, ta.h - 1), st->xy)) < _settings_game.station.station_spread) {
_deleted_stations_nearby.push_back({st->xy, st->index});
@ -2546,6 +2550,7 @@ struct SelectStationWindow : Window {
void OnInvalidateData(int data = 0, bool gui_scope = true) override
{
if (!gui_scope) return;
if (T::EXPECTED_FACIL == FACIL_WAYPOINT) _station_nearby_road_waypoint_search = (this->select_station_cmd.cmd & CMD_ID_MASK) == CMD_BUILD_ROAD_WAYPOINT;
FindStationsNearby<T>(this->area, true);
this->vscroll->SetCount((uint)_stations_nearby_list.size() + 1);
this->SetDirty();
@ -2608,6 +2613,7 @@ static bool StationJoinerNeeded(const CommandContainer &cmd, TileArea ta)
/* Test for adjacent station or station below selection.
* If adjacent-stations is disabled and we are building next to a station, do not show the selection window.
* but join the other station immediately. */
if (T::EXPECTED_FACIL == FACIL_WAYPOINT) _station_nearby_road_waypoint_search = (cmd.cmd & CMD_ID_MASK) == CMD_BUILD_ROAD_WAYPOINT;
const T *st = FindStationsNearby<T>(ta, false);
return st == nullptr && (_settings_game.station.adjacent_stations || _stations_nearby_list.size() == 0);
}

@ -193,13 +193,34 @@ static inline bool IsBusStop(TileIndex t)
return GetStationType(t) == STATION_BUS;
}
/**
* Is the station at \a t a road waypoint?
* @param t Tile to check
* @pre IsTileType(t, MP_STATION)
* @return \c true if station is a road waypoint, \c false otherwise
*/
static inline bool IsRoadWaypoint(TileIndex t)
{
return GetStationType(t) == STATION_ROADWAYPOINT;
}
/**
* Is this tile a station tile and a road waypoint?
* @param t the tile to get the information from
* @return true if and only if the tile is a road waypoint
*/
static inline bool IsRoadWaypointTile(TileIndex t)
{
return IsTileType(t, MP_STATION) && IsRoadWaypoint(t);
}
/**
* Is the station at \a t a road station?
* @param t Tile to check
* @pre IsTileType(t, MP_STATION)
* @return \c true if station at the tile is a bus top or a truck stop, \c false otherwise
* @return \c true if station at the tile is a bus stop, truck stop \c false otherwise
*/
static inline bool IsRoadStop(TileIndex t)
static inline bool IsStationRoadStop(TileIndex t)
{
assert_tile(IsTileType(t, MP_STATION), t);
return IsTruckStop(t) || IsBusStop(t);
@ -210,9 +231,31 @@ static inline bool IsRoadStop(TileIndex t)
* @param t Tile to check
* @return \c true if the tile is a station tile and a road stop
*/
static inline bool IsRoadStopTile(TileIndex t)
static inline bool IsStationRoadStopTile(TileIndex t)
{
return IsTileType(t, MP_STATION) && IsStationRoadStop(t);
}
/**
* Is the station at \a t a road station?
* @param t Tile to check
* @pre IsTileType(t, MP_STATION)
* @return \c true if station at the tile is a bus stop, truck stop or road waypoint, \c false otherwise
*/
static inline bool IsAnyRoadStop(TileIndex t)
{
assert_tile(IsTileType(t, MP_STATION), t);
return IsTruckStop(t) || IsBusStop(t) || IsRoadWaypoint(t);
}
/**
* Is tile \a t a road stop station?
* @param t Tile to check
* @return \c true if the tile is a station tile and a road stop
*/
static inline bool IsAnyRoadStopTile(TileIndex t)
{
return IsTileType(t, MP_STATION) && IsRoadStop(t);
return IsTileType(t, MP_STATION) && IsAnyRoadStop(t);
}
/**
@ -222,7 +265,7 @@ static inline bool IsRoadStopTile(TileIndex t)
*/
static inline bool IsStandardRoadStopTile(TileIndex t)
{
return IsRoadStopTile(t) && GetStationGfx(t) < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
return IsAnyRoadStopTile(t) && GetStationGfx(t) < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
}
/**
@ -232,7 +275,7 @@ static inline bool IsStandardRoadStopTile(TileIndex t)
*/
static inline bool IsDriveThroughStopTile(TileIndex t)
{
return IsRoadStopTile(t) && GetStationGfx(t) >= GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
return IsAnyRoadStopTile(t) && GetStationGfx(t) >= GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
}
/**
@ -274,13 +317,13 @@ static inline StationGfx GetAirportGfx(TileIndex t)
/**
* Gets the direction the road stop entrance points towards.
* @param t the tile of the road stop
* @pre IsRoadStopTile(t)
* @pre IsAnyRoadStopTile(t)
* @return the direction of the entrance
*/
static inline DiagDirection GetRoadStopDir(TileIndex t)
{
StationGfx gfx = GetStationGfx(t);
assert_tile(IsRoadStopTile(t), t);
assert_tile(IsAnyRoadStopTile(t), t);
if (gfx < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET) {
return (DiagDirection)(gfx);
} else {
@ -630,9 +673,9 @@ static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopTyp
* @param tram_rt the tram roadtype on this tile
* @param a the direction of the roadstop
*/
static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, Axis a)
static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, StationType rst, RoadType road_rt, RoadType tram_rt, Axis a)
{
MakeStation(t, station, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + a);
MakeStation(t, station, sid, rst, GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + a);
SetRoadTypes(t, road_rt, tram_rt);
SetRoadOwner(t, RTT_ROAD, road);
SetRoadOwner(t, RTT_TRAM, tram);

@ -41,6 +41,7 @@ enum StationType {
STATION_DOCK,
STATION_BUOY,
STATION_WAYPOINT,
STATION_ROADWAYPOINT,
};
/** Types of RoadStops */

@ -16,6 +16,7 @@
#include "../ship.h"
#include "../aircraft.h"
#include "../object_map.h"
#include "../waypoint_base.h"
#include "../string_func_extra.h"
/* Helper for filling property tables */
@ -1269,6 +1270,27 @@ class NIHStationStruct : public NIHelper {
seprintf(buffer, lastof(buffer), " Docking tiles: %X, %u x %u", st->docking_station.tile, st->docking_station.w, st->docking_station.h);
output.print(buffer);
}
const Waypoint *wp = Waypoint::GetIfValid(index);
if (wp) {
output.register_next_line_click_flag_toggle(1);
seprintf(buffer, lastof(buffer), " [%c] flags: 0x%X", output.flags & 1 ? '-' : '+', wp->waypoint_flags);
output.print(buffer);
if (output.flags & 1) {
auto print = [&](const char *name) {
seprintf(buffer, lastof(buffer), " %s", name);
output.print(buffer);
};
auto check_flag = [&](WaypointFlags flag, const char *name) {
if (HasBit(wp->waypoint_flags, flag)) print(name);
};
check_flag(WPF_HIDE_LABEL, "WPF_HIDE_LABEL");
check_flag(WPF_ROAD, "WPF_ROAD");
}
seprintf(buffer, lastof(buffer), " road_waypoint_area: tile: %X (%u x %u), width: %u, height: %u",
wp->road_waypoint_area.tile, TileX(wp->road_waypoint_area.tile), TileY(wp->road_waypoint_area.tile), wp->road_waypoint_area.w, wp->road_waypoint_area.h);
output.print(buffer);
}
}
};

@ -955,6 +955,15 @@ static const DrawTileSprites _station_display_datas_bus[] = {
TILE_SPRITE_LINE(SPR_ROAD_PAVED_STRAIGHT_Y, _station_display_datas_0171)
};
static const DrawTileSprites _station_display_datas_road_waypoint[] = {
TILE_SPRITE_LINE(SPR_BUS_STOP_NE_GROUND | (1U << PALETTE_MODIFIER_COLOUR), _station_display_datas_71)
TILE_SPRITE_LINE(SPR_BUS_STOP_SE_GROUND | (1U << PALETTE_MODIFIER_COLOUR), _station_display_datas_72)
TILE_SPRITE_LINE(SPR_BUS_STOP_SW_GROUND | (1U << PALETTE_MODIFIER_COLOUR), _station_display_datas_73)
TILE_SPRITE_LINE(SPR_BUS_STOP_NW_GROUND | (1U << PALETTE_MODIFIER_COLOUR), _station_display_datas_74)
TILE_SPRITE_LINE(SPR_ROAD_PAVED_STRAIGHT_X, _station_display_datas_0170)
TILE_SPRITE_LINE(SPR_ROAD_PAVED_STRAIGHT_Y, _station_display_datas_0171)
};
static const DrawTileSprites _station_display_datas_oilrig[] = {
TILE_SPRITE_LINE(SPR_FLAT_WATER_TILE, _station_display_nothing)
};
@ -999,4 +1008,5 @@ static const DrawTileSprites * const _station_display_datas[] = {
_station_display_datas_dock,
_station_display_datas_buoy,
_station_display_datas_waypoint,
_station_display_datas_road_waypoint,
};

@ -1301,7 +1301,7 @@ static bool CanRoadContinueIntoNextTile(const Town *t, const TileIndex tile, con
/* If the next tile is a station, allow if it's a road station facing the proper direction. Otherwise return false. */
if (IsTileType(next_tile, MP_STATION)) {
/* If the next tile is a road station, allow if it can be entered by the new tunnel/bridge, otherwise disallow. */
return IsRoadStop(next_tile) && (GetRoadStopDir(next_tile) == ReverseDiagDir(road_dir) || (IsDriveThroughStopTile(next_tile) && GetRoadStopDir(next_tile) == road_dir));
return IsAnyRoadStop(next_tile) && (GetRoadStopDir(next_tile) == ReverseDiagDir(road_dir) || (IsDriveThroughStopTile(next_tile) && GetRoadStopDir(next_tile) == road_dir));
}
/* If the next tile is a road depot, allow if it's facing the right way. */

@ -549,7 +549,8 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
}
case STATION_BUS:
case STATION_TRUCK: {
case STATION_TRUCK:
case STATION_ROADWAYPOINT: {
CommandCost ret = IsRoadStopBridgeAboveOK(tile, IsDriveThroughStopTile(tile), GetRoadStopDir(tile),
tile_start, tile_end, z_start + 1, bridge_type, transport_type);
if (ret.Failed()) {
@ -672,7 +673,8 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
}
case STATION_BUS:
case STATION_TRUCK: {
case STATION_TRUCK:
case STATION_ROADWAYPOINT: {
CommandCost ret = IsRoadStopBridgeAboveOK(tile, IsDriveThroughStopTile(tile), GetRoadStopDir(tile),
tile_start, tile_end, z_start + 1, bridge_type, transport_type);
if (ret.Failed()) {

@ -2470,6 +2470,7 @@ public:
/* check rail waypoint or buoy (no ownership) */
if ((IsRailWaypointTile(tile) && this->vli.vtype == VEH_TRAIN && IsInfraTileUsageAllowed(VEH_TRAIN, this->vli.company, tile))
|| (IsRoadWaypointTile(tile) && this->vli.vtype == VEH_ROAD && IsInfraTileUsageAllowed(VEH_ROAD, this->vli.company, tile))
|| (IsBuoyTile(tile) && this->vli.vtype == VEH_SHIP)) {
if (this->vli.type != VL_STATION_LIST) return;
if (!(Station::Get(this->vli.index)->facilities & FACIL_WAYPOINT)) return;
@ -2480,7 +2481,7 @@ public:
if (IsTileType(tile, MP_STATION)) {
if (this->vli.type != VL_STATION_LIST) return;
if (Station::Get(this->vli.index)->facilities & FACIL_WAYPOINT) return;
if (BaseStation::Get(this->vli.index)->facilities & FACIL_WAYPOINT) return;
StationID st_index = GetStationIndex(tile);
const Station *st = Station::Get(st_index);
@ -3754,7 +3755,7 @@ public:
break;
case OT_GOTO_WAYPOINT: {
assert(v->type == VEH_TRAIN || v->type == VEH_SHIP);
assert(v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP);
SetDParam(0, v->current_order.GetDestination());
str = HasBit(v->vehicle_flags, VF_PATHFINDER_LOST) ? STR_VEHICLE_STATUS_CANNOT_REACH_WAYPOINT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL;
SetDParam(1, v->GetDisplaySpeed());

@ -194,8 +194,10 @@ enum ViewportDragDropSelectionProcess {
DDSP_PLACE_ROAD_X_DIR, ///< Road placement (X axis)
DDSP_PLACE_ROAD_Y_DIR, ///< Road placement (Y axis)
DDSP_PLACE_AUTOROAD, ///< Road placement (auto)
DDSP_BUILD_ROAD_WAYPOINT, ///< Road stop placement (waypoint)
DDSP_BUILD_BUSSTOP, ///< Road stop placement (buses)
DDSP_BUILD_TRUCKSTOP, ///< Road stop placement (trucks)
DDSP_REMOVE_ROAD_WAYPOINT, ///< Road stop removal (waypoint)
DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses)
DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks)
DDSP_CONVERT_ROAD, ///< Road conversion

@ -41,6 +41,10 @@ void Waypoint::GetTileArea(TileArea *ta, StationType type) const
*ta = this->train_station;
return;
case STATION_ROADWAYPOINT:
*ta = this->road_waypoint_area;
return;
case STATION_BUOY:
ta->tile = this->xy;
ta->w = 1;

@ -17,6 +17,7 @@
*/
enum WaypointFlags {
WPF_HIDE_LABEL = 0, ///< Hide waypoint label
WPF_ROAD = 1, ///< This is a road waypoint
};
/** Representation of a waypoint. */
@ -24,6 +25,8 @@ struct Waypoint FINAL : SpecializedStation<Waypoint, true> {
uint16 town_cn; ///< The N-1th waypoint for this town (consecutive number)
uint16 waypoint_flags; ///< Waypoint flags, see WaypointFlags
TileArea road_waypoint_area; ///< Tile area the road waypoint part covers
/**
* Create a waypoint at the given tile.
* @param tile The location of the waypoint.

@ -68,13 +68,13 @@ void Waypoint::MoveSign(TileIndex new_xy)
* @param cid previous owner of the waypoint
* @return the deleted nearby waypoint
*/
static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile, StringID str, CompanyID cid)
static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile, StringID str, CompanyID cid, bool is_road)
{
Waypoint *best = nullptr;
uint thres = 8;
for (Waypoint *wp : Waypoint::Iterate()) {
if (!wp->IsInUse() && wp->string_id == str && wp->owner == cid) {
if (!wp->IsInUse() && wp->string_id == str && wp->owner == cid && HasBit(wp->waypoint_flags, WPF_ROAD) == is_road) {
uint cur_dist = DistanceManhattan(tile, wp->xy);
if (cur_dist < thres) {
@ -109,6 +109,29 @@ Axis GetAxisForNewWaypoint(TileIndex tile)
}
}
/**
* Get the axis for a new road waypoint. This means that if it is a valid
* tile to build a waypoint on it returns a valid Axis, otherwise an
* invalid one.
* @param tile the tile to look at.
* @return the axis for the to-be-build waypoint.
*/
Axis GetAxisForNewRoadWaypoint(TileIndex tile)
{
/* The axis for rail waypoints is easy. */
if (IsRoadWaypointTile(tile)) return DiagDirToAxis(GetRoadStopDir(tile));
/* Non-plain road type, no valid axis for waypoints. */
if (!IsNormalRoadTile(tile)) return INVALID_AXIS;
RoadBits bits = GetAllRoadBits(tile);
if ((bits & ROAD_Y) == 0) return AXIS_X;
if ((bits & ROAD_X) == 0) return AXIS_Y;
return INVALID_AXIS;
}
extern CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags);
/**
@ -152,7 +175,7 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *
}
extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec);
extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp);
extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road);
extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis);
extern CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, byte layout);
@ -232,15 +255,16 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint
Waypoint *wp = nullptr;
TileArea new_location(start_tile, width, height);
CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, new_location, &wp);
CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, new_location, &wp, false);
if (ret.Failed()) return ret;
/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
TileIndex center_tile = start_tile + (count / 2) * offset;
if (wp == nullptr && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company);
if (wp == nullptr && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company, false);
if (wp != nullptr) {
/* Reuse an existing waypoint. */
if (HasBit(wp->waypoint_flags, WPF_ROAD)) return CMD_ERROR;
if (wp->owner != _current_company) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT);
/* check if we want to expand an already existing waypoint? */
@ -301,6 +325,134 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint
return CommandCost(EXPENSES_CONSTRUCTION, count * _price[PR_BUILD_WAYPOINT_RAIL]);
}
/**
* Convert existing road to waypoint. Eg build a waypoint station over
* piece of road
* @param start_tile northern most tile where waypoint will be built
* @param flags Operation to perform.
* @param p1 bit 0..7: Width of the road stop.
* bit 8..15: Length of the road stop.
* bit 16: Allow stations directly adjacent to other stations.
* bit 17: #Axis of the road.
* @param p2 bit 16..31: Station ID to join (NEW_STATION if build new one).
* @param text Unused.
* @return The cost of this operation or an error.
*/
CommandCost CmdBuildRoadWaypoint(TileIndex start_tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
StationID station_to_join = GB(p2, 16, 16);
byte width = GB(p1, 0, 8);
byte height = GB(p1, 8, 8);
bool adjacent = HasBit(p1, 16);
Axis axis = Extract<Axis, 17, 1>(p1);
/* The number of parts to build */
byte count = axis == AXIS_X ? height : width;
if ((axis == AXIS_X ? width : height) != 1) return CMD_ERROR;
if (count == 0 || count > _settings_game.station.station_spread) return CMD_ERROR;
bool reuse = (station_to_join != NEW_STATION);
if (!reuse) station_to_join = INVALID_STATION;
bool distant_join = (station_to_join != INVALID_STATION);
if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR;
/* Check if the first tile and the last tile are valid */
if (!IsValidTile(start_tile) || TileAddWrap(start_tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
TileArea roadstop_area(start_tile, width, height);
/* Total road stop cost. */
CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[PR_BUILD_STATION_TRUCK]);
StationID est = INVALID_STATION;
extern CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt, bool require_road);
CommandCost ret = CheckFlatLandRoadStop(roadstop_area, flags, 5 << axis, true, STATION_ROADWAYPOINT, axis, &est, INVALID_ROADTYPE, true);
if (ret.Failed()) return ret;
cost.AddCost(ret);
Waypoint *wp = nullptr;
ret = FindJoiningWaypoint(est, station_to_join, adjacent, roadstop_area, &wp, true);
if (ret.Failed()) return ret;
/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
TileIndex center_tile = start_tile + (count / 2) * TileOffsByDiagDir(AxisToDiagDir(OtherAxis(axis)));;
if (wp == nullptr && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company, true);
if (wp != nullptr) {
/* Reuse an existing waypoint. */
if (!HasBit(wp->waypoint_flags, WPF_ROAD)) return CMD_ERROR;
if (wp->owner != _current_company) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT);
CommandCost ret = wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TEST);
if (ret.Failed()) return ret;
} else {
/* allocate and initialize new waypoint */
if (!Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
}
if (flags & DC_EXEC) {
if (wp == nullptr) {
wp = new Waypoint(start_tile);
SetBit(wp->waypoint_flags, WPF_ROAD);
} else if (!wp->IsInUse()) {
/* Move existing (recently deleted) waypoint to the new location */
wp->xy = start_tile;
}
wp->owner = _current_company;
wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TRY);
wp->delete_ctr = 0;
wp->facilities |= FACIL_BUS_STOP | FACIL_TRUCK_STOP;
wp->build_date = _date;
wp->string_id = STR_SV_STNAME_WAYPOINT;
if (wp->town == nullptr) MakeDefaultName(wp);
wp->UpdateVirtCoord();
/* Check every tile in the area. */
for (TileIndex cur_tile : roadstop_area) {
/* Get existing road types and owners before any tile clearing */
RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
DisallowedRoadDirections drd = IsNormalRoadTile(cur_tile) ? GetDisallowedRoadDirections(cur_tile) : DRD_NONE;
extern CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags);
if (IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile)) {
RemoveRoadStop(cur_tile, flags);
}
wp->road_waypoint_area.Add(cur_tile);
wp->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
/* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
* bits first. */
if (IsNormalRoadTile(cur_tile)) {
UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
}
UpdateCompanyRoadInfrastructure(road_rt, road_owner, ROAD_STOP_TRACKBIT_FACTOR);
UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, ROAD_STOP_TRACKBIT_FACTOR);
MakeDriveThroughRoadStop(cur_tile, wp->owner, road_owner, tram_owner, wp->index, STATION_ROADWAYPOINT, road_rt, tram_rt, axis);
SetDriveThroughStopDisallowedRoadDirections(cur_tile, drd);
Company::Get(wp->owner)->infrastructure.station++;
MarkTileDirtyByTile(cur_tile);
UpdateRoadCachedOneWayStatesAroundTile(cur_tile);
}
NotifyRoadLayoutChanged(true);
DirtyCompanyInfrastructureWindows(wp->owner);
}
return cost;
}
/**
* Build a buoy.
* @param tile tile where to place the buoy
@ -317,7 +469,7 @@ CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (!IsTileFlat(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE);
Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE, false);
if (wp == nullptr && !Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_WAYPOINT_BUOY]);

@ -17,6 +17,7 @@
CommandCost RemoveBuoy(TileIndex tile, DoCommandFlag flags);
Axis GetAxisForNewWaypoint(TileIndex tile);
Axis GetAxisForNewRoadWaypoint(TileIndex tile);
void ShowWaypointWindow(const Waypoint *wp);
void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype);

@ -45,7 +45,24 @@ private:
if (!this->wp->IsInUse()) return this->wp->xy;
TileArea ta;
this->wp->GetTileArea(&ta, this->vt == VEH_TRAIN ? STATION_WAYPOINT : STATION_BUOY);
StationType type;
switch (this->vt) {
case VEH_TRAIN:
type = STATION_WAYPOINT;
break;
case VEH_ROAD:
type = STATION_ROADWAYPOINT;
break;
case VEH_SHIP:
type = STATION_BUOY;
break;
default:
NOT_REACHED();
}
this->wp->GetTileArea(&ta, type);
return ta.GetCenterTile();
}
@ -58,15 +75,24 @@ public:
WaypointWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
{
this->wp = Waypoint::Get(window_number);
this->vt = (wp->string_id == STR_SV_STNAME_WAYPOINT) ? VEH_TRAIN : VEH_SHIP;
if (wp->string_id == STR_SV_STNAME_WAYPOINT) {
this->vt = HasBit(this->wp->waypoint_flags, WPF_ROAD) ? VEH_ROAD : VEH_TRAIN;
} else {
this->vt = VEH_SHIP;
}
this->CreateNestedTree();
if (this->vt == VEH_TRAIN) {
this->GetWidget<NWidgetCore>(WID_W_SHOW_VEHICLES)->SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP);
}
if (this->vt == VEH_ROAD) {
this->GetWidget<NWidgetCore>(WID_W_SHOW_VEHICLES)->SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP);
}
if (this->vt != VEH_SHIP) {
this->GetWidget<NWidgetCore>(WID_W_CENTER_VIEW)->tool_tip = STR_WAYPOINT_VIEW_CENTER_TOOLTIP;
this->GetWidget<NWidgetCore>(WID_W_RENAME)->tool_tip = STR_WAYPOINT_VIEW_CHANGE_WAYPOINT_NAME;
}
this->show_hide_label = (this->vt == VEH_TRAIN && _settings_client.gui.allow_hiding_waypoint_labels);
this->show_hide_label = (this->vt != VEH_SHIP && _settings_client.gui.allow_hiding_waypoint_labels);
this->GetWidget<NWidgetStacked>(WID_W_TOGGLE_HIDDEN_SEL)->SetDisplayedPlane(this->show_hide_label ? 0 : SZSP_NONE);
this->FinishInitNested(window_number);
@ -135,7 +161,7 @@ public:
this->SetWidgetLoweredState(WID_W_TOGGLE_HIDDEN, HasBit(this->wp->waypoint_flags, WPF_HIDE_LABEL));
bool show_hide_label = (this->vt == VEH_TRAIN && _settings_client.gui.allow_hiding_waypoint_labels);
bool show_hide_label = (this->vt != VEH_SHIP && _settings_client.gui.allow_hiding_waypoint_labels);
if (show_hide_label != this->show_hide_label) {
this->show_hide_label = show_hide_label;
this->GetWidget<NWidgetStacked>(WID_W_TOGGLE_HIDDEN_SEL)->SetDisplayedPlane(this->show_hide_label ? 0 : SZSP_NONE);

@ -19,6 +19,7 @@ enum RoadToolbarWidgets {
WID_ROT_AUTOROAD, ///< Autorail.
WID_ROT_DEMOLISH, ///< Demolish.
WID_ROT_DEPOT, ///< Build depot.
WID_ROT_BUILD_WAYPOINT, ///< Build waypoint.
WID_ROT_BUS_STATION, ///< Build bus station.
WID_ROT_TRUCK_STATION, ///< Build truck station.
WID_ROT_ONE_WAY, ///< Build one-way road.

Loading…
Cancel
Save